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:
-> 4da9c0eced358c4a2e6dc588b821d19e871f817eb05ea35882bf228bb2fb0248@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