Skip to content

Commit

Permalink
feat(core): add template registration sidechain features (#4470)
Browse files Browse the repository at this point in the history
Description
---
- adds CodeTemplateRegistration output type
- adds TemplateRegistration to SideChainFeatures
- add grpc methods for registering a code template

Motivation and Context
--- 
Code templates need to be committed to on L1.

The `binary_url` and `repo_url` fields are not validated by base node consensus because: 
1. The rust implementation of Multiaddr does not currently support paths in http addresses e.g. `/dns4/github.com/tcp/443/http/tari-project/tari.git` is not well-formed however [the spec](https://multiformats.io/multiaddr/) says it should be.
2. Url parsing is complex and adding it to consensus code could introduce bugs (general/security/DoS)

Depends on #4466 
Depends on #4496 

How Has This Been Tested?
---
TODO: add cucumber tests for this output type
  • Loading branch information
sdbondi authored Aug 29, 2022
1 parent 9153f9a commit 8ee5a05
Show file tree
Hide file tree
Showing 25 changed files with 1,048 additions and 101 deletions.
31 changes: 31 additions & 0 deletions applications/tari_app_grpc/proto/sidechain_types.proto
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,36 @@ syntax = "proto3";

package tari.rpc;

import "types.proto";

message SideChainFeatures {
oneof side_chain_features {
TemplateRegistration template_registration = 1;
}
}

message TemplateRegistration {
bytes author_public_key = 1;
Signature author_signature = 2;
string template_name = 3;
uint32 template_version = 4;
TemplateType template_type = 5;
BuildInfo build_info = 6;
bytes binary_sha = 7;
string binary_url = 8;
}

message TemplateType {
oneof template_type {
WasmInfo wasm = 1;
}
}

message WasmInfo {
uint32 abi_version = 1;
}

message BuildInfo {
string repo_url = 1;
bytes commit_hash = 2;
}
10 changes: 10 additions & 0 deletions applications/tari_app_grpc/proto/wallet.proto
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ service Wallet {
rpc ClaimShaAtomicSwapTransaction(ClaimShaAtomicSwapRequest) returns (ClaimShaAtomicSwapResponse);
// This will claim a HTLC refund transaction
rpc ClaimHtlcRefundTransaction(ClaimHtlcRefundRequest) returns (ClaimHtlcRefundResponse);
// Creates a transaction with a template registration output
rpc CreateTemplateRegistration(CreateTemplateRegistrationRequest) returns (CreateTemplateRegistrationResponse);
rpc SetBaseNode(SetBaseNodeRequest) returns (SetBaseNodeResponse);

rpc StreamTransactionEvents(TransactionEventRequest) returns (stream TransactionEventResponse);
Expand Down Expand Up @@ -99,6 +101,7 @@ message CreateBurnTransactionRequest{
string message = 3;
}


message PaymentRecipient {
string address = 1;
uint64 amount = 2;
Expand Down Expand Up @@ -259,6 +262,13 @@ message ImportUtxosResponse {
repeated uint64 tx_ids = 1;
}

message CreateTemplateRegistrationRequest {
TemplateRegistration template_registration = 1;
uint64 fee_per_gram = 2;
}

message CreateTemplateRegistrationResponse { }

message CancelTransactionRequest {
uint64 tx_id = 1;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ impl TryFrom<grpc::OutputFeatures> for OutputFeatures {
fn try_from(features: grpc::OutputFeatures) -> Result<Self, Self::Error> {
let sidechain_features = features
.sidechain_features
.and_then(|f| f.side_chain_features)
.map(SideChainFeatures::try_from)
.transpose()?;

Expand Down Expand Up @@ -64,7 +65,7 @@ impl From<OutputFeatures> for grpc::OutputFeatures {
output_type: u32::from(features.output_type.as_byte()),
maturity: features.maturity,
metadata: features.metadata,
sidechain_features: features.sidechain_features.map(|v| *v).map(Into::into),
sidechain_features: features.sidechain_features.map(Into::into),
}
}
}
127 changes: 120 additions & 7 deletions applications/tari_app_grpc/src/conversions/sidechain_features.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,22 +20,135 @@
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

use std::convert::TryFrom;
use std::convert::{TryFrom, TryInto};

use tari_core::transactions::transaction_components::SideChainFeatures;
use tari_common_types::types::{PublicKey, Signature};
use tari_core::{
consensus::MaxSizeString,
transactions::transaction_components::{BuildInfo, CodeTemplateRegistration, SideChainFeatures, TemplateType},
};
use tari_utilities::ByteArray;

use crate::tari_rpc as grpc;

//---------------------------------- SideChainFeatures --------------------------------------------//
impl From<SideChainFeatures> for grpc::SideChainFeatures {
fn from(_value: SideChainFeatures) -> Self {
Self {}
fn from(value: SideChainFeatures) -> Self {
value.into()
}
}

impl TryFrom<grpc::SideChainFeatures> for SideChainFeatures {
impl From<SideChainFeatures> for grpc::side_chain_features::SideChainFeatures {
fn from(value: SideChainFeatures) -> Self {
match value {
SideChainFeatures::TemplateRegistration(template_reg) => {
grpc::side_chain_features::SideChainFeatures::TemplateRegistration(template_reg.into())
},
}
}
}

impl TryFrom<grpc::side_chain_features::SideChainFeatures> for SideChainFeatures {
type Error = String;

fn try_from(features: grpc::side_chain_features::SideChainFeatures) -> Result<Self, Self::Error> {
match features {
grpc::side_chain_features::SideChainFeatures::TemplateRegistration(template_reg) => {
Ok(SideChainFeatures::TemplateRegistration(template_reg.try_into()?))
},
}
}
}

// -------------------------------- TemplateRegistration -------------------------------- //
impl TryFrom<grpc::TemplateRegistration> for CodeTemplateRegistration {
type Error = String;

fn try_from(value: grpc::TemplateRegistration) -> Result<Self, Self::Error> {
Ok(Self {
author_public_key: PublicKey::from_bytes(&value.author_public_key).map_err(|e| e.to_string())?,
author_signature: value
.author_signature
.map(Signature::try_from)
.ok_or("author_signature not provided")??,
template_name: MaxSizeString::try_from(value.template_name).map_err(|e| e.to_string())?,
template_version: value
.template_version
.try_into()
.map_err(|_| "Invalid template version")?,
template_type: value
.template_type
.map(TryFrom::try_from)
.ok_or("Template type not provided")??,
build_info: value
.build_info
.map(TryFrom::try_from)
.ok_or("Build info not provided")??,
binary_sha: value.binary_sha.try_into().map_err(|_| "Invalid commit sha")?,
binary_url: MaxSizeString::try_from(value.binary_url).map_err(|e| e.to_string())?,
})
}
}

impl From<CodeTemplateRegistration> for grpc::TemplateRegistration {
fn from(value: CodeTemplateRegistration) -> Self {
Self {
author_public_key: value.author_public_key.to_vec(),
author_signature: Some(value.author_signature.into()),
template_name: value.template_name.to_string(),
template_version: u32::from(value.template_version),
template_type: Some(value.template_type.into()),
build_info: Some(value.build_info.into()),
binary_sha: value.binary_sha.to_vec(),
binary_url: value.binary_url.to_string(),
}
}
}

// -------------------------------- TemplateType -------------------------------- //
impl TryFrom<grpc::TemplateType> for TemplateType {
type Error = String;

fn try_from(value: grpc::TemplateType) -> Result<Self, Self::Error> {
let template_type = value.template_type.ok_or("Template type not provided")?;
match template_type {
grpc::template_type::TemplateType::Wasm(wasm) => Ok(TemplateType::Wasm {
abi_version: wasm.abi_version.try_into().map_err(|_| "abi_version overflowed")?,
}),
}
}
}

impl From<TemplateType> for grpc::TemplateType {
fn from(value: TemplateType) -> Self {
match value {
TemplateType::Wasm { abi_version } => Self {
template_type: Some(grpc::template_type::TemplateType::Wasm(grpc::WasmInfo {
abi_version: abi_version.into(),
})),
},
}
}
}

// -------------------------------- BuildInfo -------------------------------- //

impl TryFrom<grpc::BuildInfo> for BuildInfo {
type Error = String;

fn try_from(_features: grpc::SideChainFeatures) -> Result<Self, Self::Error> {
Ok(Self {})
fn try_from(value: grpc::BuildInfo) -> Result<Self, Self::Error> {
Ok(Self {
repo_url: value.repo_url.try_into().map_err(|_| "Invalid repo url")?,
commit_hash: value.commit_hash.try_into().map_err(|_| "Invalid commit hash")?,
})
}
}

impl From<BuildInfo> for grpc::BuildInfo {
fn from(value: BuildInfo) -> Self {
Self {
repo_url: value.repo_url.into_string(),
commit_hash: value.commit_hash.into_vec(),
}
}
}
58 changes: 55 additions & 3 deletions applications/tari_console_wallet/src/grpc/wallet_grpc_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ use tari_app_grpc::{
CoinSplitResponse,
CreateBurnTransactionRequest,
CreateBurnTransactionResponse,
CreateTemplateRegistrationRequest,
CreateTemplateRegistrationResponse,
FileDeletedResponse,
GetBalanceRequest,
GetBalanceResponse,
Expand Down Expand Up @@ -89,13 +91,19 @@ use tari_common_types::{
};
use tari_comms::{multiaddr::Multiaddr, types::CommsPublicKey, CommsNode};
use tari_core::transactions::{
tari_amount::MicroTari,
transaction_components::{OutputFeatures, UnblindedOutput},
tari_amount::{MicroTari, T},
transaction_components::{
CodeTemplateRegistration,
OutputFeatures,
OutputType,
SideChainFeatures,
UnblindedOutput,
},
};
use tari_utilities::{hex::Hex, ByteArray};
use tari_wallet::{
connectivity_service::{OnlineStatus, WalletConnectivityInterface},
output_manager_service::handle::OutputManagerHandle,
output_manager_service::{handle::OutputManagerHandle, UtxoSelectionCriteria},
transaction_service::{
handle::TransactionServiceHandle,
storage::models::{self, WalletTransaction},
Expand Down Expand Up @@ -915,6 +923,50 @@ impl wallet_server::Wallet for WalletGrpcServer {

Ok(Response::new(FileDeletedResponse {}))
}

async fn create_template_registration(
&self,
request: Request<CreateTemplateRegistrationRequest>,
) -> Result<Response<CreateTemplateRegistrationResponse>, Status> {
let mut output_manager = self.wallet.output_manager_service.clone();
let mut transaction_service = self.wallet.transaction_service.clone();
let message = request.into_inner();

let template_registration = CodeTemplateRegistration::try_from(
message
.template_registration
.ok_or_else(|| Status::invalid_argument("template_registration is empty"))?,
)
.map_err(|e| Status::invalid_argument(format!("template_registration is invalid: {}", e)))?;
let fee_per_gram = message.fee_per_gram;

let message = format!("Template registration {}", template_registration.template_name);
let output = output_manager
.create_output_with_features(1 * T, OutputFeatures {
output_type: OutputType::CodeTemplateRegistration,
sidechain_features: Some(SideChainFeatures::TemplateRegistration(template_registration)),
..Default::default()
})
.await
.map_err(|e| Status::internal(e.to_string()))?;

let (tx_id, transaction) = output_manager
.create_send_to_self_with_output(vec![output], fee_per_gram.into(), UtxoSelectionCriteria::default())
.await
.map_err(|e| Status::internal(e.to_string()))?;

debug!(
target: LOG_TARGET,
"Template registration transaction: {:?}", transaction
);

let _ = transaction_service
.submit_transaction(tx_id, transaction, 0.into(), message)
.await
.map_err(|e| Status::internal(e.to_string()))?;

Ok(Response::new(CreateTemplateRegistrationResponse {}))
}
}

async fn handle_completed_tx(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ use crate::{
},
txn_schema,
};

fn setup() -> BlockchainDatabase<TempDatabase> {
create_new_blockchain()
}
Expand Down
13 changes: 13 additions & 0 deletions base_layer/core/src/consensus/consensus_encoding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,13 @@ mod hashing;
mod integers;
mod micro_tari;
mod script;
mod string;
mod vec;

use std::io;

pub use hashing::{ConsensusHasher, DomainSeparatedConsensusHasher};
pub use string::MaxSizeString;
pub use vec::MaxSizeVec;

pub use self::bytes::MaxSizeBytes;
Expand Down Expand Up @@ -93,6 +96,16 @@ impl<T: ConsensusDecoding + ?Sized> FromConsensusBytes<T> for T {
}
}

pub fn read_byte<R: io::Read>(reader: &mut R) -> Result<u8, io::Error> {
let mut buf = [0u8; 1];
reader.read_exact(&mut buf)?;
Ok(buf[0])
}

pub fn write_byte<W: io::Write>(writer: &mut W, byte: u8) -> Result<(), io::Error> {
writer.write_all(&[byte])
}

#[cfg(test)]
pub mod test {
use super::*;
Expand Down
Loading

0 comments on commit 8ee5a05

Please sign in to comment.