Rust-Rust cardano: rust-cardano — Rust implementation of Cardano primitives, helpers, and related applications

Build Status Build status Gitter chat

Rust implementation of Cardano primitives, helpers, and related applications

Cardano Rust is a modular toolbox of Cardano’s cryptographic primitives, a library of wallet functions and a future alternative Cardano node implementation written in Rust. It can be used by any third-party to build wallet applications and interact with the Cardano blockchain.

Related repositories

Installation

If this is a new installation: install rust's toolchain.

We support the following states; stable, unstable and nightly.

We also support the wasm32 target.

Building the Library

To build the library, use:

cargo build

Running the tests

To run the tests, use:

cargo test

How to integrate the Rust library in your project

Information will be available soon on crates.io

In the mean time, it is possible to add the project using git submodules:

git submodule add https://github.com/input-output-hk/rust-cardano cardano-deps

And then by adding the following to your Cargo.toml:

[dependencies]
cardano = { path = "cardano-deps/cardano" }

Comments

  • cardano-cli uses a fork of dialoguer
    cardano-cli uses a fork of dialoguer

    Aug 22, 2018

    This is needed since #214 as we needed to allow empty passwords. We can start reusing mainstream dialoguer as soon as mitsuhiko/dialoguer#6 is merged and released.

    P - low 
    Reply
  • send transaction err: Blockchain protocol error
    send transaction err: Blockchain protocol error

    Nov 16, 2018

    i got some ada here, and want to send a transaction from code below.

    extern crate cardano;
    extern crate cbor_event;
    extern crate exe_common;
    extern crate rand;
    
    use cardano::address::ExtendedAddr;
    use cardano::bip::bip44::AddrType;
    use cardano::config::ProtocolMagic;
    use cardano::coin;
    use cardano::hdwallet::{DerivationScheme, XPrv};
    use cardano::util::hex;
    use cardano::wallet;
    use cardano::tx::{self, TxoPointer, TxOut, TxInWitness};
    use cardano::{fee::LinearFee, txbuild::{self, TxBuilder, TxFinalized}};
    use cardano::txutils::OutputPolicy;
    use exe_common::network::api::Api;
    use rand::{Rng, os::OsRng};
    
    use std::{collections::BTreeSet, error, fmt, str::FromStr};
    
    fn test_transaction() {
        let addr_1_private_key_hex = "some_hex_private_key";
        let _addr_2_private_key_hex = "some_hex_private_key";
        let _addr_3_private_key_hex = "some_hex_private_key";
        let _addr_1 = "Ae2tdPwUPEZ5zKCZR29WGLMb1hWQPg1mf7RiDrxGbqs89dMMjWd6os74JXR";
        let addr_2 = "Ae2tdPwUPEZL2ibp8BXpn5xuewkbxSPpaw6WccAZBtrZRWX2u6mwBuQkyCi";
        let addr_3 = "Ae2tdPwUPEZ8Nc35XganVXMQR8gQbBrxehGrNJsXQDjPxCdEciYCopcWmiu";
    
    
        let mut peer = get_peer();
    
        let tx_id_str = "4da9c0eced358c4a2e6dc588b821d19e871f817eb05ea35882bf228bb2fb0248";
        let vout_index: u32 = 1;
        let tx_id: tx::TxId = match FromStr::from_str(tx_id_str) {
            Ok(i) => i,
            Err(e) => {
                println!("get txid from string err: {}", e);
                return;
            }
        };
        let input_amount = coin::Coin::new(20000000).unwrap();
    
        let private_key = get_private_key_from_hex(addr_1_private_key_hex);
    
    
        let mut builder = TxBuilder::new();
        let ptr = TxoPointer { id: tx_id, index: vout_index };
        builder.add_input(&ptr, input_amount);
    
        let out_address: ExtendedAddr = FromStr::from_str(addr_2).unwrap();
        let out_amount = coin::Coin::new(10000000).unwrap();
        let out = TxOut { address: out_address, value: out_amount };
        builder.add_output_value(&out);
    
        let fee_algorithm = LinearFee::default();
        let change_address: ExtendedAddr = FromStr::from_str(addr_3).unwrap();
        let _result = builder.add_output_policy(&fee_algorithm, &OutputPolicy::One(change_address)).map_err(Error::ErrorWhenApplyingOutputPolicy);
    
        let tx = builder.make_tx().map_err(Error::CannotBuildTxFromBuilder).unwrap();
        let mut finalized = TxFinalized::new(tx.clone());
    
        let txid = tx.id();
        let witness = TxInWitness::new(ProtocolMagic::default(), &private_key, &txid);
        match finalized.add_witness(witness) {
            Ok(_) => {}
            Err(e) => {
                println!("add witness err: {}", e);
                return;
            }
        }
    
        let tx_aux = finalized.make_txaux().unwrap();
        println!("{}", tx_aux);
    
        match verify(tx_aux.clone(), ProtocolMagic::default()) {
            Ok(_) => {}
            Err(e) => {
                println!("verify transaction err: {}", e);
                return;
            }
        };
    
        println!("sending transaction {}", tx_aux.tx.id());
    
    
        match peer.send_transaction(tx_aux) {
            Ok(r) => println!("{:?}", r),
            Err(e) => {
                println!("send transaction err: {}", e);
                return;
            }
        };
    }
    
    #[allow(dead_code)]
    fn verify(tx_aux: cardano::tx::TxAux, protocol_magic: ProtocolMagic) -> Result<(), cardano::block::verify::Error>
    {
        // check that there are inputs
        if tx_aux.tx.inputs.is_empty() {
            return Err(cardano::block::verify::Error::NoInputs);
        }
    
        // check that there are outputs
        if tx_aux.tx.outputs.is_empty() {
            return Err(cardano::block::verify::Error::NoOutputs);
        }
    
        // check that there are no duplicate inputs
        let mut inputs = BTreeSet::new();
        if !tx_aux.tx.inputs.iter().all(|x| inputs.insert(x)) {
            return Err(cardano::block::verify::Error::DuplicateInputs);
        }
    
        // check that all outputs have a non-zero amount
        if !tx_aux.tx.outputs.iter().all(|x| x.value > coin::Coin::zero()) {
            return Err(cardano::block::verify::Error::ZeroCoin);
        }
    
        // Note: we don't need to check against MAX_COIN because Coin's
        // constructor already has.
    
        // check that none of the outputs are redeem addresses
        if tx_aux.tx.outputs.iter().any(|x| x.address.addr_type == cardano::address::AddrType::ATRedeem) {
            return Err(cardano::block::verify::Error::RedeemOutput);
        }
    
        // TODO: check address attributes?
    
        // verify transaction witnesses
        if tx_aux.tx.inputs.len() < tx_aux.witness.len() {
            return Err(cardano::block::verify::Error::UnexpectedWitnesses);
        }
    
        if tx_aux.tx.inputs.len() > tx_aux.witness.len() {
            return Err(cardano::block::verify::Error::MissingWitnesses);
        }
    
        tx_aux.witness.iter().try_for_each(|in_witness| {
            if !in_witness.verify_tx(protocol_magic, &tx_aux.tx) {
                return Err(cardano::block::verify::Error::BadTxWitness);
            }
            Ok(())
        })?;
    
        // verify that txids of redeem inputs correspond to the redeem pubkey
        for (txin, in_witness) in tx_aux.tx.inputs.iter().zip(tx_aux.witness.iter()) {
            if let tx::TxInWitness::RedeemWitness(pubkey, _) = in_witness {
                if tx::redeem_pubkey_to_txid(&pubkey).0 != txin.id {
                    return Err(cardano::block::verify::Error::WrongRedeemTxId);
                }
            }
        }
    
        Ok(())
    }
    
    fn get_peer() -> exe_common::network::Peer {
        let native_peer = exe_common::config::net::Peer::native("relays.cardano-mainnet.iohk.io:3000".to_string());
        assert!(native_peer.is_native());
        exe_common::network::Peer::new("mainnet".to_string(), "iohk-hosts".to_string(), native_peer, ProtocolMagic::default()).unwrap()
    }
    
    #[derive(Debug)]
    pub enum Error {
        /// happened user attempts to finalize a transaction already
        /// in a finalizing state
        CannotFinalizeAFinalizedTransaction,
    
        CannotAddWitnessesToAnOpenedTransaction,
    
        CannotAddMoreWitnessesThanInputs,
    
        CannotAddInputsToAFinalizedTransaction,
    
        CannotAddOutputToAFinalizedTransaction,
    
        CannotAddChangeToAFinalizedTransaction,
    
        TransactionNotFinalized,
    
        /// when input is duplicated in the transaction
        DoubleSpend,
    
        CannotRemoveInputInputNotFound,
        CannotRemoveOutput,
        CannotRemoveChangeChangeNotFound,
    
        /// TODO: this is temporary only until we can support selection
        /// policy with multiple change addresses.
        ///
        /// In the mean time we need to ask users to remove the previous output
        /// or to keep the current one.
        MoreThanOneChangeAddressIsNotSupportedYet,
    
        ErrorWhenApplyingOutputPolicy(txbuild::Error),
    
        CannotBuildTxFromBuilder(txbuild::Error),
    }
    
    impl fmt::Display for Error {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            match self {
                Error::CannotFinalizeAFinalizedTransaction => write!(f, "Transaction is already in a finalized state"),
                Error::CannotAddWitnessesToAnOpenedTransaction => write!(f, "Transaction is not finalized, finalize the transaction before adding witnesses"),
                Error::CannotAddMoreWitnessesThanInputs => write!(f, "There is already enough witness for the transaction, cannot add more witnesses than inputs."),
                Error::CannotAddInputsToAFinalizedTransaction => write!(f, "Transaction is in a finalized state, cannot add more inputs"),
                Error::CannotAddOutputToAFinalizedTransaction => write!(f, "Transaction is in a finalized state, cannot add more outputs"),
                Error::CannotAddChangeToAFinalizedTransaction => write!(f, "Transaction is in a finalized state, cannot add more change addresses"),
                Error::TransactionNotFinalized => write!(f, "Transaction is not finalized, finalize it first"),
                Error::DoubleSpend => write!(f, "Input already used in the transaction"),
                Error::CannotRemoveInputInputNotFound => write!(f, "Cannot remove input, input not found"),
                Error::CannotRemoveOutput => write!(f, "Cannot remove output, output not found"),
                Error::CannotRemoveChangeChangeNotFound => write!(f, "Cannot remove change, change address not found"),
                Error::MoreThanOneChangeAddressIsNotSupportedYet => write!(f, "Cannot add more than one output address for now, this feature is not yet supported"),
                Error::ErrorWhenApplyingOutputPolicy(_) => write!(f, "Error when applying the output policy utilising the changes"),
                Error::CannotBuildTxFromBuilder(_) => write!(f, "Error when constructing the Tx, invalid data."),
            }
        }
    }
    
    impl error::Error for Error {
        fn cause(&self) -> Option<&error::Error> {
            match self {
                Error::CannotFinalizeAFinalizedTransaction => None,
                Error::CannotAddWitnessesToAnOpenedTransaction => None,
                Error::CannotAddMoreWitnessesThanInputs => None,
                Error::CannotAddInputsToAFinalizedTransaction => None,
                Error::CannotAddOutputToAFinalizedTransaction => None,
                Error::CannotAddChangeToAFinalizedTransaction => None,
                Error::TransactionNotFinalized => None,
                Error::DoubleSpend => None,
                Error::CannotRemoveInputInputNotFound => None,
                Error::CannotRemoveOutput => None,
                Error::CannotRemoveChangeChangeNotFound => None,
                Error::MoreThanOneChangeAddressIsNotSupportedYet => None,
                Error::ErrorWhenApplyingOutputPolicy(ref err) => Some(err),
                Error::CannotBuildTxFromBuilder(ref err) => Some(err),
            }
        }
    }
    
    

    but i got this:

    Tx:
    -> 4[email protected]0
       Ae2tdPwUPEZL2ibp8BXpn5xuewkbxSPpaw6WccAZBtrZRWX2u6mwBuQkyCi -> 10.000000 ->
       Ae2tdPwUPEZ8Nc35XganVXMQR8gQbBrxehGrNJsXQDjPxCdEciYCopcWmiu -> 9.832006 ->
    
    witnesses: TxWitness([PkWitness(e03ce300a9420757ef52119f957b368eb82557c71133ca366ad70118e7ef5b40410dd5d59e4e3a1e0938d022788024ce2cd2893b18bb7cfd3ecf2b48a87c6619, 1dae87de5890a8fe4372949df93683f1175dfaedbb46942230581d69a7906293bf4f96693da9f30e87c5a1abca9b72c1c99d347fe41c3e1fd9ccb91041120400)])
    
    
    sending transaction eaf96ec8143e787e2f16f0c4fb3e9e69438a2ae90e6ed7b5d513ba4716f79c2f
    send transaction err: Blockchain protocol error
    

    so what's wrong with my code?

    X - help wanted X - question 
    Reply
  • Duplication?
    Duplication?

    Sep 19, 2018

    Hi, there seems to be duplication in the cardano/ code, there is an HDWallet.rs file and also a wallet/ dir containing similar code. Is there a different purpose? Which should one use?

    X - question 
    Reply
  • artifacting of test results
    artifacting of test results

    Jan 9, 2019

    updates to test runs to explictly archive test results

    Testing 
    Reply
  • `change_value` is calculated wrong
    `change_value` is calculated wrong

    Sep 12, 2018

    Hello!

    This line of code: https://github.com/input-output-hk/rust-cardano/blob/master/cardano/src/input_selection.rs#L124

    Calculates change_value as (output_value - input_value - estimated_fee). This is wrong, since if my transaction contains 5 coins of inputs, 2 coins of outputs, and fee of 0.17 - then change will be calculated as (2 - 5 - 0.17) = -3.17.

    It should be (input_value - output_value - estimated_fee), so then if will be calculated as (5 - 2 - 0.17) = 2.83 which is the change.

    Currently change does not work, and it's confirmed in this thread: https://forum.cardano.org/t/rust-code-cardano-cli-change-address-fees-not-working-as-expected/15897

    B - Bug D - medium P - high 
    Reply
  • Made block streaming interruptible
    Made block streaming interruptible

    May 24, 2019

    Added internal functionality that allows to interrupt block-streaming when necessary. But it's not used for now. Required for later rollback processing.

    Part of https://github.com/input-output-hk/rust-cardano/pull/693#issuecomment-494574901

    Reply