Fix timestamp and cancel functionality
- Also serialize and send helper fn
This commit is contained in:
parent
3ffc7aa5bc
commit
f636408647
|
@ -208,8 +208,15 @@ fn main() -> Result<(), Box<error::Error>> {
|
||||||
SubCommand::with_name("send-timestamp")
|
SubCommand::with_name("send-timestamp")
|
||||||
.about("Send a timestamp to unlock a transfer")
|
.about("Send a timestamp to unlock a transfer")
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name("process-id")
|
Arg::with_name("to")
|
||||||
.index(1)
|
.index(1)
|
||||||
|
.value_name("PUBKEY")
|
||||||
|
.takes_value(true)
|
||||||
|
.required(true)
|
||||||
|
.help("The pubkey of recipient"),
|
||||||
|
).arg(
|
||||||
|
Arg::with_name("process-id")
|
||||||
|
.index(2)
|
||||||
.value_name("PROCESS_ID")
|
.value_name("PROCESS_ID")
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.required(true)
|
.required(true)
|
||||||
|
|
|
@ -333,7 +333,7 @@ mod test {
|
||||||
contract.pubkey(),
|
contract.pubkey(),
|
||||||
dt,
|
dt,
|
||||||
from.pubkey(),
|
from.pubkey(),
|
||||||
true,
|
None,
|
||||||
1,
|
1,
|
||||||
Hash::default(),
|
Hash::default(),
|
||||||
);
|
);
|
||||||
|
@ -408,7 +408,7 @@ mod test {
|
||||||
contract.pubkey(),
|
contract.pubkey(),
|
||||||
dt,
|
dt,
|
||||||
from.pubkey(),
|
from.pubkey(),
|
||||||
true,
|
Some(from.pubkey()),
|
||||||
1,
|
1,
|
||||||
Hash::default(),
|
Hash::default(),
|
||||||
);
|
);
|
||||||
|
@ -474,7 +474,7 @@ mod test {
|
||||||
contract.pubkey(),
|
contract.pubkey(),
|
||||||
Utc::now(),
|
Utc::now(),
|
||||||
from.pubkey(),
|
from.pubkey(),
|
||||||
true,
|
None,
|
||||||
1,
|
1,
|
||||||
Hash::default(),
|
Hash::default(),
|
||||||
);
|
);
|
||||||
|
@ -528,7 +528,7 @@ mod test {
|
||||||
contract,
|
contract,
|
||||||
date,
|
date,
|
||||||
keypair.pubkey(),
|
keypair.pubkey(),
|
||||||
true,
|
Some(keypair.pubkey()),
|
||||||
192,
|
192,
|
||||||
Hash::default(),
|
Hash::default(),
|
||||||
);
|
);
|
||||||
|
|
|
@ -153,12 +153,11 @@ impl Transaction {
|
||||||
contract: Pubkey,
|
contract: Pubkey,
|
||||||
dt: DateTime<Utc>,
|
dt: DateTime<Utc>,
|
||||||
dt_pubkey: Pubkey,
|
dt_pubkey: Pubkey,
|
||||||
cancelable: bool,
|
cancelable: Option<Pubkey>,
|
||||||
tokens: i64,
|
tokens: i64,
|
||||||
last_id: Hash,
|
last_id: Hash,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let from = from_keypair.pubkey();
|
let budget = if let Some(from) = cancelable {
|
||||||
let budget = if cancelable {
|
|
||||||
Budget::Or(
|
Budget::Or(
|
||||||
(Condition::Timestamp(dt, dt_pubkey), Payment { tokens, to }),
|
(Condition::Timestamp(dt, dt_pubkey), Payment { tokens, to }),
|
||||||
(Condition::Signature(from), Payment { tokens, to: from }),
|
(Condition::Signature(from), Payment { tokens, to: from }),
|
||||||
|
|
159
src/wallet.rs
159
src/wallet.rs
|
@ -1,5 +1,6 @@
|
||||||
use bincode::{deserialize, serialize};
|
use bincode::{deserialize, serialize};
|
||||||
use bs58;
|
use bs58;
|
||||||
|
use budget_program::BudgetState;
|
||||||
use chrono::prelude::*;
|
use chrono::prelude::*;
|
||||||
use clap::ArgMatches;
|
use clap::ArgMatches;
|
||||||
use crdt::NodeInfo;
|
use crdt::NodeInfo;
|
||||||
|
@ -37,9 +38,10 @@ pub enum WalletCommand {
|
||||||
Option<DateTime<Utc>>,
|
Option<DateTime<Utc>>,
|
||||||
Option<Pubkey>,
|
Option<Pubkey>,
|
||||||
Option<Vec<Pubkey>>,
|
Option<Vec<Pubkey>>,
|
||||||
Option<bool>,
|
Option<Pubkey>,
|
||||||
),
|
),
|
||||||
TimeElapsed(Pubkey, DateTime<Utc>),
|
// TimeElapsed(to, process_id, timestamp)
|
||||||
|
TimeElapsed(Pubkey, Pubkey, DateTime<Utc>),
|
||||||
Witness(Pubkey),
|
Witness(Pubkey),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -188,7 +190,7 @@ pub fn parse_command(
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
let cancelable = if pay_matches.is_present("cancelable") {
|
let cancelable = if pay_matches.is_present("cancelable") {
|
||||||
Some(true)
|
Some(pubkey)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
@ -215,6 +217,16 @@ pub fn parse_command(
|
||||||
Ok(WalletCommand::Witness(process_id))
|
Ok(WalletCommand::Witness(process_id))
|
||||||
}
|
}
|
||||||
("send-timestamp", Some(timestamp_matches)) => {
|
("send-timestamp", Some(timestamp_matches)) => {
|
||||||
|
let pubkey_vec = bs58::decode(timestamp_matches.value_of("to").unwrap())
|
||||||
|
.into_vec()
|
||||||
|
.expect("base58-encoded public key");
|
||||||
|
|
||||||
|
if pubkey_vec.len() != mem::size_of::<Pubkey>() {
|
||||||
|
eprintln!("{}", timestamp_matches.usage());
|
||||||
|
Err(WalletError::BadParameter("Invalid public key".to_string()))?;
|
||||||
|
}
|
||||||
|
let to = Pubkey::new(&pubkey_vec);
|
||||||
|
|
||||||
let pubkey_vec = bs58::decode(timestamp_matches.value_of("process-id").unwrap())
|
let pubkey_vec = bs58::decode(timestamp_matches.value_of("process-id").unwrap())
|
||||||
.into_vec()
|
.into_vec()
|
||||||
.expect("base58-encoded public key");
|
.expect("base58-encoded public key");
|
||||||
|
@ -239,7 +251,7 @@ pub fn parse_command(
|
||||||
} else {
|
} else {
|
||||||
Utc::now()
|
Utc::now()
|
||||||
};
|
};
|
||||||
Ok(WalletCommand::TimeElapsed(process_id, dt))
|
Ok(WalletCommand::TimeElapsed(to, process_id, dt))
|
||||||
}
|
}
|
||||||
("", None) => {
|
("", None) => {
|
||||||
println!("{}", matches.usage());
|
println!("{}", matches.usage());
|
||||||
|
@ -312,33 +324,11 @@ pub fn process_command(config: &WalletConfig) -> Result<String, Box<error::Error
|
||||||
}
|
}
|
||||||
// Cancel a contract by contract Pubkey
|
// Cancel a contract by contract Pubkey
|
||||||
WalletCommand::Cancel(pubkey) => {
|
WalletCommand::Cancel(pubkey) => {
|
||||||
let result = WalletRpcRequest::GetLastId.make_rpc_request(&config.rpc_addr, 1, None)?;
|
let last_id = get_last_id(&config)?;
|
||||||
if result.as_str().is_none() {
|
|
||||||
Err(WalletError::RpcRequestError(
|
|
||||||
"Received bad last_id".to_string(),
|
|
||||||
))?
|
|
||||||
}
|
|
||||||
let last_id_str = result.as_str().unwrap();
|
|
||||||
let last_id_vec = bs58::decode(last_id_str)
|
|
||||||
.into_vec()
|
|
||||||
.map_err(|_| WalletError::RpcRequestError("Received bad last_id".to_string()))?;
|
|
||||||
let last_id = Hash::new(&last_id_vec);
|
|
||||||
|
|
||||||
let tx =
|
let tx =
|
||||||
Transaction::budget_new_signature(&config.id, pubkey, config.id.pubkey(), last_id);
|
Transaction::budget_new_signature(&config.id, pubkey, config.id.pubkey(), last_id);
|
||||||
let serialized = serialize(&tx).unwrap();
|
let signature_str = serialize_and_send_tx(&config, &tx)?;
|
||||||
let params = json!(serialized);
|
|
||||||
let signature = WalletRpcRequest::SendTransaction.make_rpc_request(
|
|
||||||
&config.rpc_addr,
|
|
||||||
2,
|
|
||||||
Some(params),
|
|
||||||
)?;
|
|
||||||
if signature.as_str().is_none() {
|
|
||||||
Err(WalletError::RpcRequestError(
|
|
||||||
"Received result of an unexpected type".to_string(),
|
|
||||||
))?
|
|
||||||
}
|
|
||||||
let signature_str = signature.as_str().unwrap();
|
|
||||||
|
|
||||||
Ok(signature_str.to_string())
|
Ok(signature_str.to_string())
|
||||||
}
|
}
|
||||||
|
@ -364,58 +354,69 @@ pub fn process_command(config: &WalletConfig) -> Result<String, Box<error::Error
|
||||||
// If client has positive balance, pay tokens to another address
|
// If client has positive balance, pay tokens to another address
|
||||||
WalletCommand::Pay(tokens, to, timestamp, timestamp_pubkey, ref witnesses, cancelable) => {
|
WalletCommand::Pay(tokens, to, timestamp, timestamp_pubkey, ref witnesses, cancelable) => {
|
||||||
let last_id = get_last_id(&config)?;
|
let last_id = get_last_id(&config)?;
|
||||||
let cancelable_bool = cancelable.is_some();
|
|
||||||
|
|
||||||
if timestamp == None && *witnesses == None {
|
if timestamp == None && *witnesses == None {
|
||||||
let tx = Transaction::new(&config.id, to, tokens, last_id);
|
let tx = Transaction::new(&config.id, to, tokens, last_id);
|
||||||
let serialized = serialize(&tx).unwrap();
|
let signature_str = serialize_and_send_tx(&config, &tx)?;
|
||||||
let params = json!(serialized);
|
|
||||||
let signature = WalletRpcRequest::SendTransaction.make_rpc_request(
|
|
||||||
&config.rpc_addr,
|
|
||||||
2,
|
|
||||||
Some(params),
|
|
||||||
)?;
|
|
||||||
if signature.as_str().is_none() {
|
|
||||||
Err(WalletError::RpcRequestError(
|
|
||||||
"Received result of an unexpected type".to_string(),
|
|
||||||
))?
|
|
||||||
}
|
|
||||||
let signature_str = signature.as_str().unwrap();
|
|
||||||
|
|
||||||
Ok(signature_str.to_string())
|
Ok(signature_str.to_string())
|
||||||
} else if *witnesses == None {
|
} else if *witnesses == None {
|
||||||
|
// Amazingly, this works!
|
||||||
let dt = timestamp.unwrap();
|
let dt = timestamp.unwrap();
|
||||||
let dt_pubkey = match timestamp_pubkey {
|
let dt_pubkey = match timestamp_pubkey {
|
||||||
Some(pubkey) => pubkey,
|
Some(pubkey) => pubkey,
|
||||||
None => config.id.pubkey(),
|
None => config.id.pubkey(),
|
||||||
};
|
};
|
||||||
let process_id = Keypair::new().pubkey();
|
|
||||||
let tx = Transaction::budget_new_on_date(
|
let contract_funds = Keypair::new();
|
||||||
|
// println!("Contract Funds: {:?}", contract_funds.pubkey());
|
||||||
|
let contract_state = Keypair::new();
|
||||||
|
// println!("Contract State: {:?}", contract_state.pubkey());
|
||||||
|
let budget_program_id = BudgetState::id();
|
||||||
|
// println!("Budget ProgramId: {:?}", budget_program_id);
|
||||||
|
|
||||||
|
// Create account for contract funds
|
||||||
|
let tx = Transaction::system_create(
|
||||||
&config.id,
|
&config.id,
|
||||||
|
contract_funds.pubkey(),
|
||||||
|
last_id,
|
||||||
|
tokens,
|
||||||
|
0,
|
||||||
|
budget_program_id,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
let _signature_str = serialize_and_send_tx(&config, &tx)?;
|
||||||
|
// println!("Create Contract Funds Acct: {:?}", signature_str);
|
||||||
|
|
||||||
|
// Create account for contract state
|
||||||
|
let tx = Transaction::system_create(
|
||||||
|
&config.id,
|
||||||
|
contract_state.pubkey(),
|
||||||
|
last_id,
|
||||||
|
1,
|
||||||
|
196,
|
||||||
|
budget_program_id,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
let _signature_str = serialize_and_send_tx(&config, &tx)?;
|
||||||
|
// println!("Create Contract State Acct: {:?}", signature_str);
|
||||||
|
|
||||||
|
// Initializing contract
|
||||||
|
let tx = Transaction::budget_new_on_date(
|
||||||
|
&contract_funds,
|
||||||
to,
|
to,
|
||||||
process_id,
|
contract_state.pubkey(),
|
||||||
dt,
|
dt,
|
||||||
dt_pubkey,
|
dt_pubkey,
|
||||||
cancelable_bool,
|
cancelable,
|
||||||
tokens,
|
tokens,
|
||||||
last_id,
|
last_id,
|
||||||
);
|
);
|
||||||
let serialized = serialize(&tx).unwrap();
|
let signature_str = serialize_and_send_tx(&config, &tx)?;
|
||||||
let params = json!(serialized);
|
// println!("Initialize Contract: {:?}", signature_str);
|
||||||
let signature = WalletRpcRequest::SendTransaction.make_rpc_request(
|
|
||||||
&config.rpc_addr,
|
|
||||||
2,
|
|
||||||
Some(params),
|
|
||||||
)?;
|
|
||||||
if signature.as_str().is_none() {
|
|
||||||
Err(WalletError::RpcRequestError(
|
|
||||||
"Received result of an unexpected type".to_string(),
|
|
||||||
))?
|
|
||||||
}
|
|
||||||
let signature_str = signature.as_str().unwrap();
|
|
||||||
Ok(json!({
|
Ok(json!({
|
||||||
"signature": signature_str,
|
"signature": signature_str,
|
||||||
"processId": process_id,
|
"processId": format!("{}", contract_state.pubkey()),
|
||||||
}).to_string())
|
}).to_string())
|
||||||
} else if timestamp == None {
|
} else if timestamp == None {
|
||||||
Ok("Witness Txs not yet handled".to_string())
|
Ok("Witness Txs not yet handled".to_string())
|
||||||
|
@ -424,29 +425,18 @@ pub fn process_command(config: &WalletConfig) -> Result<String, Box<error::Error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Apply time elapsed to contract
|
// Apply time elapsed to contract
|
||||||
WalletCommand::TimeElapsed(pubkey, dt) => {
|
WalletCommand::TimeElapsed(to, pubkey, dt) => {
|
||||||
let last_id = get_last_id(&config)?;
|
let last_id = get_last_id(&config)?;
|
||||||
|
|
||||||
let tx = Transaction::budget_new_timestamp(
|
let tx = Transaction::budget_new_timestamp(
|
||||||
&config.id,
|
&config.id,
|
||||||
pubkey,
|
pubkey,
|
||||||
config.id.pubkey(),
|
to,
|
||||||
dt,
|
dt,
|
||||||
last_id,
|
last_id,
|
||||||
);
|
);
|
||||||
let serialized = serialize(&tx).unwrap();
|
// println!("{:?}", tx);
|
||||||
let params = json!(serialized);
|
let signature_str = serialize_and_send_tx(&config, &tx)?;
|
||||||
let signature = WalletRpcRequest::SendTransaction.make_rpc_request(
|
|
||||||
&config.rpc_addr,
|
|
||||||
2,
|
|
||||||
Some(params),
|
|
||||||
)?;
|
|
||||||
if signature.as_str().is_none() {
|
|
||||||
Err(WalletError::RpcRequestError(
|
|
||||||
"Received result of an unexpected type".to_string(),
|
|
||||||
))?
|
|
||||||
}
|
|
||||||
let signature_str = signature.as_str().unwrap();
|
|
||||||
|
|
||||||
Ok(signature_str.to_string())
|
Ok(signature_str.to_string())
|
||||||
}
|
}
|
||||||
|
@ -600,6 +590,23 @@ fn get_last_id(config: &WalletConfig) -> Result<Hash, Box<error::Error>> {
|
||||||
Ok(Hash::new(&last_id_vec))
|
Ok(Hash::new(&last_id_vec))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn serialize_and_send_tx(config: &WalletConfig, tx: &Transaction) -> Result<String, Box<error::Error>> {
|
||||||
|
// println!("{:?}", tx);
|
||||||
|
let serialized = serialize(tx).unwrap();
|
||||||
|
let params = json!(serialized);
|
||||||
|
let signature = WalletRpcRequest::SendTransaction.make_rpc_request(
|
||||||
|
&config.rpc_addr,
|
||||||
|
2,
|
||||||
|
Some(params),
|
||||||
|
)?;
|
||||||
|
if signature.as_str().is_none() {
|
||||||
|
Err(WalletError::RpcRequestError(
|
||||||
|
"Received result of an unexpected type".to_string(),
|
||||||
|
))?
|
||||||
|
}
|
||||||
|
Ok(signature.as_str().unwrap().to_string())
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
Loading…
Reference in New Issue