Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: add committee management utxo #3835

Merged
merged 17 commits into from
Feb 18, 2022
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion applications/tari_app_grpc/proto/types.proto
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ message TransactionOutput {
uint32 version = 9;
}

// Options for UTXO's
// Options for UTXOs
message OutputFeatures {
// Flags are the feature flags that differentiate between outputs, eg Coinbase all of which has different rules
uint32 flags = 1;
Expand All @@ -225,6 +225,7 @@ message OutputFeatures {
SideChainCheckpointFeatures sidechain_checkpoint = 8;
// Version
uint32 version = 9;
CommitteeCheckpointFeatures committee_checkpoint = 10;
}

message AssetOutputFeatures {
Expand All @@ -249,6 +250,10 @@ message SideChainCheckpointFeatures {
repeated bytes committee = 2;
}

message CommitteeCheckpointFeatures {
repeated bytes committee = 1;
uint64 effective_sidechain_height = 2;
}

// The components of the block or transaction. The same struct can be used for either, since in Mimblewimble,
// cut-through means that blocks and transactions have the same structure. The inputs, outputs and kernels should
Expand Down
11 changes: 11 additions & 0 deletions applications/tari_app_grpc/proto/wallet.proto
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ service Wallet {
rpc CreateInitialAssetCheckpoint(CreateInitialAssetCheckpointRequest) returns (CreateInitialAssetCheckpointResponse);
// TODO: Needs a better name pls
rpc CreateFollowOnAssetCheckpoint(CreateFollowOnAssetCheckpointRequest) returns (CreateFollowOnAssetCheckpointResponse);
rpc CreateCommitteeCheckpoint(CreateCommitteeCheckpointRequest) returns (CreateCommitteeCheckpointResponse);

rpc GetOwnedAssets(Empty) returns (GetOwnedAssetsResponse);

Expand Down Expand Up @@ -280,6 +281,16 @@ message CreateFollowOnAssetCheckpointResponse {

}

message CreateCommitteeCheckpointRequest {
bytes asset_public_key = 1;
repeated bytes committee = 2;
uint64 effective_sidechain_height = 3;
}

message CreateCommitteeCheckpointResponse {

}

message GetOwnedAssetsResponse {
repeated Asset assets = 1;
}
Expand Down
34 changes: 33 additions & 1 deletion applications/tari_app_grpc/src/conversions/output_features.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ use tari_common_types::{
};
use tari_core::transactions::transaction_components::{
AssetOutputFeatures,
CommitteeCheckpointFeatures,
MintNonFungibleFeatures,
OutputFeatures,
OutputFeaturesVersion,
Expand Down Expand Up @@ -66,7 +67,8 @@ impl TryFrom<grpc::OutputFeatures> for OutputFeatures {
parent_public_key,
features.asset.map(|a| a.try_into()).transpose()?,
features.mint_non_fungible.map(|m| m.try_into()).transpose()?,
features.sidechain_checkpoint.map(|m| m.try_into()).transpose()?,
features.sidechain_checkpoint.map(|s| s.try_into()).transpose()?,
features.committee_checkpoint.map(|c| c.try_into()).transpose()?,
))
}
}
Expand All @@ -86,6 +88,7 @@ impl From<OutputFeatures> for grpc::OutputFeatures {
mint_non_fungible: features.mint_non_fungible.map(|m| m.into()),
sidechain_checkpoint: features.sidechain_checkpoint.map(|m| m.into()),
version: features.version as u32,
committee_checkpoint: features.committee_checkpoint.map(|c| c.into()),
}
}
}
Expand Down Expand Up @@ -184,3 +187,32 @@ impl TryFrom<grpc::SideChainCheckpointFeatures> for SideChainCheckpointFeatures
Ok(Self { merkle_root, committee })
}
}

impl From<CommitteeCheckpointFeatures> for grpc::CommitteeCheckpointFeatures {
fn from(value: CommitteeCheckpointFeatures) -> Self {
Self {
committee: value.committee.iter().map(|c| c.as_bytes().to_vec()).collect(),
effective_sidechain_height: value.effective_sidechain_height,
}
}
}

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

fn try_from(value: grpc::CommitteeCheckpointFeatures) -> Result<Self, Self::Error> {
let committee = value
.committee
.iter()
.map(|c| {
PublicKey::from_bytes(c).map_err(|err| format!("committee member was not a valid public key: {}", err))
})
.collect::<Result<_, _>>()?;
let effective_sidechain_height = value.effective_sidechain_height;

Ok(Self {
committee,
effective_sidechain_height,
})
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ impl BaseNodeClient {
pub async fn connect(endpoint: String) -> Result<Self, CollectiblesError> {
let client = grpc::base_node_client::BaseNodeClient::connect(endpoint.clone())
.await
.map_err(|err| CollectiblesError::ClientConnectionError {
.map_err(|err| CollectiblesError::ClientConnection {
client: "wallet",
address: endpoint,
error: err.to_string(),
Expand All @@ -57,14 +57,14 @@ impl BaseNodeClient {
.list_asset_registrations(request)
.await
.map(|response| response.into_inner())
.map_err(|source| CollectiblesError::ClientRequestError {
.map_err(|source| CollectiblesError::ClientRequest {
request: "list_asset_registrations".to_string(),
source,
})?;

let mut assets = vec![];
while let Some(result) = stream.next().await {
let asset = result.map_err(|source| CollectiblesError::ClientRequestError {
let asset = result.map_err(|source| CollectiblesError::ClientRequest {
request: "list_asset_registrations".to_string(),
source,
})?;
Expand All @@ -87,7 +87,7 @@ impl BaseNodeClient {
.get_asset_metadata(request)
.await
.map(|response| response.into_inner())
.map_err(|s| CollectiblesError::ClientRequestError {
.map_err(|s| CollectiblesError::ClientRequest {
request: "get_asset_metadata".to_string(),
source: s,
})?;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ impl GrpcValidatorNodeClient {
let s = Self {
client: grpc::validator_node_client::ValidatorNodeClient::connect(endpoint.clone())
.await
.map_err(|e| CollectiblesError::ClientConnectionError {
.map_err(|e| CollectiblesError::ClientConnection {
client: "validator_node",
address: endpoint,
error: e.to_string(),
Expand Down Expand Up @@ -69,7 +69,7 @@ impl GrpcValidatorNodeClient {
.map_err(|e| {
error!(target: LOG_TARGET, "{}", e);

CollectiblesError::ClientRequestError {
CollectiblesError::ClientRequest {
source: e,
request: "invoke_read_method".to_string(),
}
Expand Down Expand Up @@ -100,7 +100,7 @@ impl GrpcValidatorNodeClient {
.map_err(|e| {
error!(target: LOG_TARGET, "{}", e);

CollectiblesError::ClientRequestError {
CollectiblesError::ClientRequest {
source: e,
request: "invoke_method".to_string(),
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ impl WalletClient {
let dst = format!("http://{}", self.endpoint);
let client = grpc::wallet_client::WalletClient::connect(dst)
.await
.map_err(|err| CollectiblesError::ClientConnectionError {
.map_err(|err| CollectiblesError::ClientConnection {
client: "wallet",
address: self.endpoint.clone(),
error: err.to_string(),
Expand All @@ -55,6 +55,15 @@ impl WalletClient {
Ok(())
}

fn get_inner(
delta1 marked this conversation as resolved.
Show resolved Hide resolved
&mut self,
) -> Result<&mut grpc::wallet_client::WalletClient<tonic::transport::Channel>, CollectiblesError>
{
let inner = self.inner.as_mut().ok_or(CollectiblesError::NoConnection)?;

Ok(inner)
}

pub async fn register_asset(
&mut self,
name: String,
Expand All @@ -64,7 +73,7 @@ impl WalletClient {
template_ids_implemented: Vec<u32>,
template_parameters: Vec<grpc::TemplateParameter>,
) -> Result<String, CollectiblesError> {
let inner = self.inner.as_mut().unwrap();
let inner = self.get_inner()?;
let request = RegisterAssetRequest {
name,
public_key: public_key.as_bytes().into(),
Expand All @@ -73,64 +82,93 @@ impl WalletClient {
image,
template_parameters,
};
let result = inner.register_asset(request).await.map_err(|error| {
CollectiblesError::ClientRequestError {
request: "register_asset".to_string(),
source: error,
}
})?;
let result =
inner
.register_asset(request)
.await
.map_err(|error| CollectiblesError::ClientRequest {
request: "register_asset".to_string(),
source: error,
})?;
debug!(target: LOG_TARGET, "result {:?}", result);
Ok(result.into_inner().public_key.to_hex())
}

pub async fn list_owned_assets(
&mut self,
) -> Result<grpc::GetOwnedAssetsResponse, CollectiblesError> {
let inner = self.inner.as_mut().unwrap();
let inner = self.get_inner()?;
let request = grpc::Empty {};
let result = inner.get_owned_assets(request).await.map_err(|source| {
CollectiblesError::ClientRequestError {
request: "get_owned_assets".to_string(),
source,
}
})?;
let result =
inner
.get_owned_assets(request)
.await
.map_err(|source| CollectiblesError::ClientRequest {
request: "get_owned_assets".to_string(),
source,
})?;
debug!(target: LOG_TARGET, "result {:?}", result);
Ok(result.into_inner())
}

pub async fn create_initial_asset_checkpoint(
&mut self,
asset_public_key: String,
asset_public_key: &str,
merkle_root: Vec<u8>,
committee: Vec<String>,
) -> Result<grpc::CreateInitialAssetCheckpointResponse, CollectiblesError> {
let inner = self.inner.as_mut().unwrap();
let inner = self.get_inner()?;
let committee = vec![];
let request = grpc::CreateInitialAssetCheckpointRequest {
asset_public_key: Vec::from_hex(&asset_public_key).unwrap(),
asset_public_key: Vec::from_hex(asset_public_key)?,
merkle_root,
committee: committee
.iter()
.map(|s| Vec::from_hex(s).unwrap())
.collect(),
committee,
};
let result = inner
.create_initial_asset_checkpoint(request)
.await
.map_err(|source| CollectiblesError::ClientRequestError {
.map_err(|source| CollectiblesError::ClientRequest {
request: "create_initial_asset_checkpoint".to_string(),
source,
})?;
debug!(target: LOG_TARGET, "result {:?}", result);
Ok(result.into_inner())
}

pub async fn create_committee_checkpoint(
&mut self,
asset_public_key: &str,
committee: Vec<String>,
effective_sidechain_height: u64,
) -> Result<grpc::CreateCommitteeCheckpointResponse, CollectiblesError> {
let inner = self.get_inner()?;
let committee = committee
.iter()
.map(|s| Vec::from_hex(s))
.collect::<Result<Vec<_>, _>>()?;

let request = grpc::CreateCommitteeCheckpointRequest {
asset_public_key: Vec::from_hex(asset_public_key)?,
committee,
effective_sidechain_height,
};
let result = inner
.create_committee_checkpoint(request)
.await
.map_err(|source| CollectiblesError::ClientRequest {
request: "create_committee_checkpoint".to_string(),
source,
})?;
debug!(target: LOG_TARGET, "result {:?}", result);
Ok(result.into_inner())
}

pub async fn get_unspent_amounts(
&mut self,
) -> Result<grpc::GetUnspentAmountsResponse, CollectiblesError> {
let inner = self.inner.as_mut().unwrap();
let inner = self.get_inner()?;
let request = grpc::Empty {};
let result = inner.get_unspent_amounts(request).await.map_err(|source| {
CollectiblesError::ClientRequestError {
CollectiblesError::ClientRequest {
request: "get_unspent_amounts".to_string(),
source,
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -237,21 +237,39 @@ pub(crate) async fn assets_list_registered_assets(

#[tauri::command]
pub(crate) async fn assets_create_initial_checkpoint(
asset_pub_key: String,
committee: Vec<String>,
asset_public_key: String,
state: tauri::State<'_, ConcurrentAppState>,
) -> Result<(), Status> {
let mmr = MerkleMountainRange::<Blake256, _>::new(MemBackendVec::new());

let root = mmr.get_merkle_root().unwrap();
let merkle_root = mmr.get_merkle_root().unwrap();

let mut client = state.create_wallet_client().await;
client.connect().await?;

// todo: check for enough utxos first

// create asset reg checkpoint
client
.create_initial_asset_checkpoint(asset_pub_key, root, committee)
.await
.unwrap();
.create_initial_asset_checkpoint(&asset_public_key, merkle_root)
.await?;

Ok(())
}

#[tauri::command]
pub(crate) async fn assets_create_committee_checkpoint(
asset_public_key: String,
committee: Vec<String>,
state: tauri::State<'_, ConcurrentAppState>,
) -> Result<(), Status> {
let mut client = state.create_wallet_client().await;
client.connect().await?;

// TODO: effective sidechain height...
client
.create_committee_checkpoint(&asset_public_key, committee, 0)
.await?;

Ok(())
}
Expand Down
9 changes: 7 additions & 2 deletions applications/tari_collectibles/src-tauri/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,21 @@
// 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 tari_utilities::hex::HexError;
use tonic::Status;

#[derive(Debug, thiserror::Error)]
pub enum CollectiblesError {
#[error("No connection to {client}. Is it running with grpc on '{address}' ? Error: {error}")]
ClientConnectionError {
ClientConnection {
client: &'static str,
address: String,
error: String,
},
#[error("Error invoking operation: {request}: {source}")]
ClientRequestError { request: String, source: Status },
ClientRequest { request: String, source: Status },
#[error("Error trying to use wallet client before calling connect()")]
NoConnection,
#[error("Error converting from Hex to Public Key")]
Hex(#[from] HexError),
}
Loading