Compare commits

...

13 Commits
v0.9.0 ... v0.9

20 changed files with 564 additions and 43 deletions

View File

@ -105,16 +105,16 @@ serde_cbor = "0.9.0"
serde_derive = "1.0.27" serde_derive = "1.0.27"
serde_json = "1.0.10" serde_json = "1.0.10"
socket2 = "0.3.8" socket2 = "0.3.8"
solana_program_interface = { path = "common" } solana_program_interface = { path = "common", version="0.1.0" }
sys-info = "0.5.6" sys-info = "0.5.6"
tokio = "0.1" tokio = "0.1"
tokio-codec = "0.1" tokio-codec = "0.1"
untrusted = "0.6.2" untrusted = "0.6.2"
[dev-dependencies] [dev-dependencies]
noop = { path = "programs/noop" } noop = { path = "programs/noop", version="0.1.0" }
print = { path = "programs/print" } print = { path = "programs/print", version="0.1.0" }
move_funds = { path = "programs/move_funds" } move_funds = { path = "programs/move_funds", version="0.1.0" }
[[bench]] [[bench]]
name = "bank" name = "bank"

View File

@ -0,0 +1,7 @@
steps:
- command: "ci/snap.sh"
timeout_in_minutes: 40
name: "snap [public]"
- command: "ci/docker-solana/build.sh"
timeout_in_minutes: 20
name: "docker-solana"

View File

@ -1,4 +0,0 @@
steps:
- command: "ci/snap.sh"
timeout_in_minutes: 40
name: "snap [public]"

View File

@ -39,7 +39,7 @@ steps:
- command: "ci/publish-crate.sh" - command: "ci/publish-crate.sh"
timeout_in_minutes: 20 timeout_in_minutes: 20
name: "publish crate [public]" name: "publish crate [public]"
- trigger: "solana-snap" - trigger: "solana-secondary"
branches: "!pull/*" branches: "!pull/*"
async: true async: true
build: build:

1
ci/docker-solana/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
cargo-install/

View File

@ -0,0 +1,13 @@
FROM debian:stretch
# JSON RPC port
EXPOSE 8899/tcp
# Install libssl
RUN apt update && \
apt-get install -y libssl-dev && \
rm -rf /var/lib/apt/lists/*
COPY usr/bin /usr/bin/
ENTRYPOINT [ "/usr/bin/solana-entrypoint.sh" ]
CMD [""]

View File

@ -0,0 +1,17 @@
## Minimal Solana Docker image
This image is automatically updated by CI
https://hub.docker.com/r/solanalabs/solana/
### Usage:
Run the latest beta image:
```bash
$ docker run --rm -p 8899:8899 solanalabs/solana:beta
```
Run the latest edge image:
```bash
$ docker run --rm -p 8899:8899 solanalabs/solana:edge
```
Port *8899* is the JSON RPC port, which is used by clients to communicate with the network.

38
ci/docker-solana/build.sh Executable file
View File

@ -0,0 +1,38 @@
#!/bin/bash -ex
cd "$(dirname "$0")"
eval "$(../channel-info.sh)"
if [[ $BUILDKITE_BRANCH = "$STABLE_CHANNEL" ]]; then
CHANNEL=stable
elif [[ $BUILDKITE_BRANCH = "$EDGE_CHANNEL" ]]; then
CHANNEL=edge
elif [[ $BUILDKITE_BRANCH = "$BETA_CHANNEL" ]]; then
CHANNEL=beta
fi
if [[ -z $CHANNEL ]]; then
echo Unable to determine channel to publish into, exiting.
exit 0
fi
rm -rf usr/
../docker-run.sh solanalabs/rust:1.29.1 \
cargo install --path . --root ci/docker-solana/usr
cp -f entrypoint.sh usr/bin/solana-entrypoint.sh
docker build -t solanalabs/solana:$CHANNEL .
maybeEcho=
if [[ -z $CI ]]; then
echo "Not CI, skipping |docker push|"
maybeEcho="echo"
else
(
set +x
if [[ -n $DOCKER_PASSWORD && -n $DOCKER_USERNAME ]]; then
echo "$DOCKER_PASSWORD" | docker login --username "$DOCKER_USERNAME" --password-stdin
fi
)
fi
$maybeEcho docker push solanalabs/solana:$CHANNEL

23
ci/docker-solana/entrypoint.sh Executable file
View File

@ -0,0 +1,23 @@
#!/bin/bash -ex
export RUST_LOG=solana=info
export RUST_BACKTRACE=1
solana-keygen -o /config/leader-keypair.json
solana-keygen -o /config/drone-keypair.json
solana-genesis --tokens=1000000000 --ledger /ledger < /config/drone-keypair.json
solana-fullnode-config --keypair=/config/leader-keypair.json -l > /config/leader-config.json
solana-drone --keypair /config/drone-keypair.json --network 127.0.0.1:8001 &
drone=$!
solana-fullnode --identity /config/leader-config.json --ledger /ledger/ &
fullnode=$!
abort() {
kill "$drone" "$fullnode"
}
trap abort SIGINT SIGTERM
wait "$fullnode"
kill "$drone" "$fullnode"

View File

@ -10,14 +10,14 @@ fi
eval "$(ci/channel-info.sh)" eval "$(ci/channel-info.sh)"
if [[ $BUILDKITE_BRANCH = "$STABLE_CHANNEL" ]]; then if [[ $BUILDKITE_BRANCH = "$STABLE_CHANNEL" ]]; then
SNAP_CHANNEL=stable CHANNEL=stable
elif [[ $BUILDKITE_BRANCH = "$EDGE_CHANNEL" ]]; then elif [[ $BUILDKITE_BRANCH = "$EDGE_CHANNEL" ]]; then
SNAP_CHANNEL=edge CHANNEL=edge
elif [[ $BUILDKITE_BRANCH = "$BETA_CHANNEL" ]]; then elif [[ $BUILDKITE_BRANCH = "$BETA_CHANNEL" ]]; then
SNAP_CHANNEL=beta CHANNEL=beta
fi fi
if [[ -z $SNAP_CHANNEL ]]; then if [[ -z $CHANNEL ]]; then
echo Unable to determine channel to publish into, exiting. echo Unable to determine channel to publish into, exiting.
exit 0 exit 0
fi fi
@ -51,11 +51,11 @@ if [[ ! -x /usr/bin/multilog ]]; then
sudo apt-get install -y daemontools sudo apt-get install -y daemontools
fi fi
echo --- build: $SNAP_CHANNEL channel echo --- build: $CHANNEL channel
snapcraft snapcraft
source ci/upload_ci_artifact.sh source ci/upload_ci_artifact.sh
upload_ci_artifact solana_*.snap upload_ci_artifact solana_*.snap
echo --- publish: $SNAP_CHANNEL channel echo --- publish: $CHANNEL channel
$DRYRUN snapcraft push solana_*.snap --release $SNAP_CHANNEL $DRYRUN snapcraft push solana_*.snap --release $CHANNEL

View File

@ -31,11 +31,7 @@ __cloud_FindInstances() {
declare name zone publicIp privateIp status declare name zone publicIp privateIp status
while read -r name publicIp privateIp status; do while read -r name publicIp privateIp status; do
if [[ $status != RUNNING ]]; then printf "%-30s | publicIp=%-16s privateIp=%s staus=%s\n" "$name" "$publicIp" "$privateIp" "$status"
echo "Warning: $name is not RUNNING, ignoring it."
continue
fi
printf "%-30s | publicIp=%-16s privateIp=%s\n" "$name" "$publicIp" "$privateIp"
instances+=("$name:$publicIp:$privateIp") instances+=("$name:$publicIp:$privateIp")
done < <(gcloud compute instances list \ done < <(gcloud compute instances list \

View File

@ -13,8 +13,8 @@ sysctl -w kernel.sysrq=$(( $(cat /proc/sys/kernel/sysrq) | 64 ))
if command -v earlyoom; then if command -v earlyoom; then
systemctl status earlyoom systemctl status earlyoom
else else
wget http://ftp.us.debian.org/debian/pool/main/e/earlyoom/earlyoom_1.1-2_amd64.deb wget -r -l1 -np http://ftp.us.debian.org/debian/pool/main/e/earlyoom/ -A 'earlyoom_1.1-*_amd64.deb' -e robots=off -nd
apt install --quiet --yes ./earlyoom_1.1-2_amd64.deb apt install --quiet --yes ./earlyoom_1.1-*_amd64.deb
cat > earlyoom <<OOM cat > earlyoom <<OOM
# use the kernel OOM killer, trigger at 20% available RAM, # use the kernel OOM killer, trigger at 20% available RAM,

View File

@ -31,6 +31,7 @@ use system_transaction::SystemTransaction;
use tictactoe_dashboard_program::TicTacToeDashboardProgram; use tictactoe_dashboard_program::TicTacToeDashboardProgram;
use tictactoe_program::TicTacToeProgram; use tictactoe_program::TicTacToeProgram;
use timing::{duration_as_us, timestamp}; use timing::{duration_as_us, timestamp};
use token_program::TokenProgram;
use transaction::Transaction; use transaction::Transaction;
use window::WINDOW_SIZE; use window::WINDOW_SIZE;
@ -410,6 +411,10 @@ impl Bank {
if TicTacToeDashboardProgram::process_transaction(&tx, accounts).is_err() { if TicTacToeDashboardProgram::process_transaction(&tx, accounts).is_err() {
return Err(BankError::ProgramRuntimeError); return Err(BankError::ProgramRuntimeError);
} }
} else if TokenProgram::check_id(&tx.program_id) {
if TokenProgram::process_transaction(&tx, accounts).is_err() {
return Err(BankError::ProgramRuntimeError);
}
} else if self.loaded_contract(&tx, accounts) { } else if self.loaded_contract(&tx, accounts) {
} else { } else {
return Err(BankError::UnknownContractId); return Err(BankError::UnknownContractId);

View File

@ -4,7 +4,6 @@
use bank::Bank; use bank::Bank;
use bincode::deserialize; use bincode::deserialize;
use budget_transaction::BudgetTransaction;
use counter::Counter; use counter::Counter;
use entry::Entry; use entry::Entry;
use log::Level; use log::Level;
@ -224,7 +223,7 @@ impl BankingStage {
.zip(vers) .zip(vers)
.filter_map(|(tx, ver)| match tx { .filter_map(|(tx, ver)| match tx {
None => None, None => None,
Some((tx, _addr)) => if tx.verify_plan() && ver != 0 { Some((tx, _addr)) => if ver != 0 {
Some(tx) Some(tx)
} else { } else {
None None

View File

@ -526,7 +526,7 @@ impl Crdt {
received_index: u64, received_index: u64,
) -> Result<()> { ) -> Result<()> {
if broadcast_table.is_empty() { if broadcast_table.is_empty() {
warn!("{}:not enough peers in crdt table", me.id); debug!("{}:not enough peers in crdt table", me.id);
inc_new_counter_info!("crdt-broadcast-not_enough_peers_error", 1); inc_new_counter_info!("crdt-broadcast-not_enough_peers_error", 1);
Err(CrdtError::NoPeers)?; Err(CrdtError::NoPeers)?;
} }

View File

@ -63,6 +63,7 @@ pub mod thin_client;
pub mod tictactoe_dashboard_program; pub mod tictactoe_dashboard_program;
pub mod tictactoe_program; pub mod tictactoe_program;
pub mod timing; pub mod timing;
pub mod token_program;
pub mod tpu; pub mod tpu;
pub mod transaction; pub mod transaction;
pub mod tvu; pub mod tvu;

View File

@ -22,14 +22,14 @@ impl TicTacToeDashboardProgram {
if input.len() < 2 { if input.len() < 2 {
Err(Error::InvalidUserdata)?; Err(Error::InvalidUserdata)?;
} }
let len = input[0] as usize + (0xFF * input[1] as usize); let len = input[0] as usize + (0x100 * input[1] as usize);
if len == 0 { if len == 0 {
Ok(TicTacToeDashboardProgram::default()) Ok(TicTacToeDashboardProgram::default())
} else if input.len() < len + 2 { } else if input.len() < len + 2 {
Err(Error::InvalidUserdata) Err(Error::InvalidUserdata)
} else { } else {
serde_cbor::from_slice(&input[2..(2 + len)]).map_err(|err| { serde_cbor::from_slice(&input[2..(2 + len)]).map_err(|err| {
error!("Unable to deserialize game: {:?}", err); error!("Unable to deserialize: {:?}", err);
Error::InvalidUserdata Error::InvalidUserdata
}) })
} }
@ -137,13 +137,16 @@ mod test {
#[test] #[test]
pub fn serde() { pub fn serde() {
let mut dashboard1 = TicTacToeDashboardProgram::default(); let mut dashboard1 = TicTacToeDashboardProgram::default();
dashboard1.total = 123; dashboard1.total = 1234567890;
dashboard1.pending = Pubkey::new(&[100; 32]);
for i in 1..5 {
dashboard1.completed.push(Pubkey::new(&[100 + i; 32]));
}
let mut userdata = vec![0xff; 256]; let mut userdata = vec![0xff; 512];
dashboard1.serialize(&mut userdata).unwrap(); dashboard1.serialize(&mut userdata).unwrap();
let dashboard2 = TicTacToeDashboardProgram::deserialize(&userdata).unwrap(); let dashboard2 = TicTacToeDashboardProgram::deserialize(&userdata).unwrap();
assert_eq!(dashboard1, dashboard2); assert_eq!(dashboard1, dashboard2);
} }

View File

@ -12,6 +12,7 @@ pub enum Error {
InvalidArguments, InvalidArguments,
InvalidMove, InvalidMove,
InvalidUserdata, InvalidUserdata,
InvalidTimestamp,
NoGame, NoGame,
NotYourTurn, NotYourTurn,
PlayerNotFound, PlayerNotFound,
@ -74,7 +75,7 @@ impl Game {
#[cfg(test)] #[cfg(test)]
pub fn new(player_x: Pubkey, player_o: Pubkey) -> Game { pub fn new(player_x: Pubkey, player_o: Pubkey) -> Game {
let mut game = Game::create(&player_x); let mut game = Game::create(&player_x);
game.join(player_o, 0).unwrap(); game.join(player_o, 1).unwrap();
game game
} }
@ -82,8 +83,13 @@ impl Game {
if self.state == State::Waiting { if self.state == State::Waiting {
self.player_o = Some(player_o); self.player_o = Some(player_o);
self.state = State::XMove; self.state = State::XMove;
if timestamp <= self.keep_alive[1] {
Err(Error::InvalidTimestamp)
} else {
self.keep_alive[1] = timestamp; self.keep_alive[1] = timestamp;
Ok(()) Ok(())
}
} else { } else {
Err(Error::GameInProgress) Err(Error::GameInProgress)
} }
@ -143,13 +149,25 @@ impl Game {
} }
pub fn keep_alive(self: &mut Game, player: Pubkey, timestamp: i64) -> Result<()> { pub fn keep_alive(self: &mut Game, player: Pubkey, timestamp: i64) -> Result<()> {
match self.state {
State::Waiting | State::XMove | State::OMove => {
if player == self.player_x { if player == self.player_x {
if timestamp <= self.keep_alive[0] {
Err(Error::InvalidTimestamp)?;
}
self.keep_alive[0] = timestamp; self.keep_alive[0] = timestamp;
} else if Some(player) == self.player_o { } else if Some(player) == self.player_o {
if timestamp <= self.keep_alive[1] {
Err(Error::InvalidTimestamp)?;
}
self.keep_alive[1] = timestamp; self.keep_alive[1] = timestamp;
} else { } else {
Err(Error::PlayerNotFound)?; Err(Error::PlayerNotFound)?;
} }
}
// Ignore keep_alive when game is no longer in progress
State::XWon | State::OWon | State::Draw => {}
};
Ok(()) Ok(())
} }
} }

404
src/token_program.rs Normal file
View File

@ -0,0 +1,404 @@
//! ERC20-like Token program
use bincode;
use solana_program_interface::account::Account;
use solana_program_interface::pubkey::Pubkey;
use std;
use transaction::Transaction;
#[derive(Debug, PartialEq)]
pub enum Error {
InvalidArgument,
InsufficentFunds,
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "error")
}
}
impl std::error::Error for Error {}
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug, Default, Serialize, Deserialize, PartialEq)]
pub struct TokenInfo {
/**
* Total supply of tokens
*/
supply: u64,
/**
* Number of base 10 digits to the right of the decimal place in the total supply
*/
decimals: u8,
/**
* Descriptive name of this token
*/
name: String,
/**
* Symbol for this token
*/
symbol: String,
}
#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
pub struct TokenAccountInfo {
/**
* The kind of token this account holds
*/
token: Pubkey,
/**
* Owner of this account
*/
owner: Pubkey,
/**
* Amount of tokens this account holds
*/
amount: u64,
/**
* The source account for the tokens.
*
* If `source` is None, `amount` belongs to this account.
* If `source` is Option<>, `amount` represents an allowance of tokens that
* may be transferred from the `source` account.
*/
source: Option<Pubkey>,
}
#[derive(Debug, Serialize, Deserialize, PartialEq)]
enum Command {
NewToken(TokenInfo),
NewTokenAccount(),
Transfer(u64),
Approve(u64),
}
#[derive(Debug, Serialize, Deserialize, PartialEq)]
pub enum TokenProgram {
Unallocated,
Token(TokenInfo),
Account(TokenAccountInfo),
Invalid,
}
impl Default for TokenProgram {
fn default() -> TokenProgram {
TokenProgram::Unallocated
}
}
pub const TOKEN_PROGRAM_ID: [u8; 32] = [
5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
];
impl TokenProgram {
fn to_invalid_args(err: std::boxed::Box<bincode::ErrorKind>) -> Error {
warn!("invalid argument: {:?}", err);
Error::InvalidArgument
}
fn deserialize(input: &[u8]) -> Result<TokenProgram> {
if input.len() < 1 {
Err(Error::InvalidArgument)?;
}
match input[0] {
0 => Ok(TokenProgram::Unallocated),
1 => Ok(TokenProgram::Token(
bincode::deserialize(&input[1..]).map_err(Self::to_invalid_args)?,
)),
2 => Ok(TokenProgram::Account(
bincode::deserialize(&input[1..]).map_err(Self::to_invalid_args)?,
)),
_ => Err(Error::InvalidArgument),
}
}
fn serialize(self: &TokenProgram, output: &mut [u8]) -> Result<()> {
if output.len() == 0 {
warn!("serialize fail: ouput.len is 0");
Err(Error::InvalidArgument)?;
}
match self {
TokenProgram::Unallocated | TokenProgram::Invalid => Err(Error::InvalidArgument),
TokenProgram::Token(token_info) => {
output[0] = 1;
let writer = std::io::BufWriter::new(&mut output[1..]);
bincode::serialize_into(writer, &token_info).map_err(Self::to_invalid_args)
}
TokenProgram::Account(account_info) => {
output[0] = 2;
let writer = std::io::BufWriter::new(&mut output[1..]);
bincode::serialize_into(writer, &account_info).map_err(Self::to_invalid_args)
}
}
}
pub fn check_id(program_id: &Pubkey) -> bool {
program_id.as_ref() == TOKEN_PROGRAM_ID
}
pub fn id() -> Pubkey {
Pubkey::new(&TOKEN_PROGRAM_ID)
}
pub fn process_transaction(tx: &Transaction, accounts: &mut [Account]) -> Result<()> {
let command =
bincode::deserialize::<Command>(&tx.userdata).map_err(Self::to_invalid_args)?;
info!("process_transaction: command={:?}", command);
let input_program_accounts: Vec<TokenProgram> = accounts
.iter()
.map(|account| {
if Self::check_id(&account.program_id) {
Self::deserialize(&account.userdata)
.map_err(|err| {
info!("deserialize failed: {:?}", err);
TokenProgram::Invalid
}).unwrap()
} else {
TokenProgram::Invalid
}
}).collect();
//let mut output_program_accounts: Vec<(usize, TokenProgram)> = vec![];
let mut output_program_accounts: Vec<(_, _)> = vec![];
match command {
Command::NewToken(token_info) => {
if accounts.len() != 2 {
error!("Expected 2 accounts");
Err(Error::InvalidArgument)?;
}
if let TokenProgram::Account(dest_account) = &input_program_accounts[1] {
if tx.keys[0] != dest_account.token {
info!("account 1 token mismatch");
Err(Error::InvalidArgument)?;
}
if dest_account.source.is_some() {
info!("account 1 is a delegate and cannot accept tokens");
Err(Error::InvalidArgument)?;
}
let mut output_dest_account = dest_account.clone();
output_dest_account.amount = token_info.supply;
output_program_accounts.push((1, TokenProgram::Account(output_dest_account)));
} else {
info!("account 1 invalid");
Err(Error::InvalidArgument)?;
}
if input_program_accounts[0] != TokenProgram::Unallocated {
info!("account 0 not available");
Err(Error::InvalidArgument)?;
}
output_program_accounts.push((0, TokenProgram::Token(token_info)));
}
Command::NewTokenAccount() => {
// key 0 - Destination new token account
// key 1 - Owner of the account
// key 2 - Token this account is associated with
// key 3 - Source account that this account is a delegate for (optional)
if accounts.len() < 3 {
error!("Expected 3 accounts");
Err(Error::InvalidArgument)?;
}
if input_program_accounts[0] != TokenProgram::Unallocated {
info!("account 0 is already allocated");
Err(Error::InvalidArgument)?;
}
let mut token_account_info = TokenAccountInfo {
token: tx.keys[2],
owner: tx.keys[1],
amount: 0,
source: None,
};
if accounts.len() >= 4 {
token_account_info.source = Some(tx.keys[3]);
}
output_program_accounts.push((0, TokenProgram::Account(token_account_info)));
}
Command::Transfer(amount) => {
if accounts.len() < 3 {
error!("Expected 3 accounts");
Err(Error::InvalidArgument)?;
}
if let (
TokenProgram::Account(source_account),
TokenProgram::Account(dest_account),
) = (&input_program_accounts[1], &input_program_accounts[2])
{
if source_account.token != dest_account.token {
info!("account 1/2 token mismatch");
Err(Error::InvalidArgument)?;
}
if dest_account.source.is_some() {
info!("account 2 is a delegate and cannot accept tokens");
Err(Error::InvalidArgument)?;
}
if source_account.owner != tx.keys[0] {
info!("owner of account 1 not present");
Err(Error::InvalidArgument)?;
}
if source_account.amount < amount {
Err(Error::InsufficentFunds)?;
}
let mut output_source_account = source_account.clone();
output_source_account.amount -= amount;
output_program_accounts.push((1, TokenProgram::Account(output_source_account)));
if source_account.source.is_some() {
if accounts.len() != 4 {
error!("Expected 4 accounts");
Err(Error::InvalidArgument)?;
}
let delegate_account = source_account;
if let TokenProgram::Account(source_account) = &input_program_accounts[3] {
if source_account.token != delegate_account.token {
info!("account 1/3 token mismatch");
Err(Error::InvalidArgument)?;
}
if delegate_account.source != Some(tx.keys[3]) {
info!("Account 1 is not a delegate of account 3");
Err(Error::InvalidArgument)?;
}
if source_account.amount < amount {
Err(Error::InsufficentFunds)?;
}
let mut output_source_account = source_account.clone();
output_source_account.amount -= amount;
output_program_accounts
.push((3, TokenProgram::Account(output_source_account)));
} else {
info!("account 3 is an invalid account");
Err(Error::InvalidArgument)?;
}
}
let mut output_dest_account = dest_account.clone();
output_dest_account.amount += amount;
output_program_accounts.push((2, TokenProgram::Account(output_dest_account)));
} else {
info!("account 1 and/or 2 are invalid accounts");
Err(Error::InvalidArgument)?;
}
}
Command::Approve(amount) => {
if accounts.len() != 3 {
error!("Expected 3 accounts");
Err(Error::InvalidArgument)?;
}
if let (
TokenProgram::Account(source_account),
TokenProgram::Account(delegate_account),
) = (&input_program_accounts[1], &input_program_accounts[2])
{
if source_account.token != delegate_account.token {
info!("account 1/2 token mismatch");
Err(Error::InvalidArgument)?;
}
if source_account.owner != tx.keys[0] {
info!("owner of account 1 not present");
Err(Error::InvalidArgument)?;
}
if source_account.source.is_some() {
info!("account 1 is a delegate");
Err(Error::InvalidArgument)?;
}
if delegate_account.source != Some(tx.keys[1]) {
info!("account 2 is not a delegate of account 1");
Err(Error::InvalidArgument)?;
}
let mut output_delegate_account = delegate_account.clone();
output_delegate_account.amount = amount;
output_program_accounts
.push((2, TokenProgram::Account(output_delegate_account)));
} else {
info!("account 1 and/or 2 are invalid accounts");
Err(Error::InvalidArgument)?;
}
}
}
for (index, program_account) in output_program_accounts.iter() {
info!(
"output_program_account: index={} userdata={:?}",
index, program_account
);
Self::serialize(program_account, &mut accounts[*index].userdata)?;
}
Ok(())
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
pub fn serde() {
assert_eq!(TokenProgram::deserialize(&[0]), Ok(TokenProgram::default()));
let mut userdata = vec![0; 256];
let account = TokenProgram::Account(TokenAccountInfo {
token: Pubkey::new(&[1; 32]),
owner: Pubkey::new(&[2; 32]),
amount: 123,
source: None,
});
assert!(account.serialize(&mut userdata).is_ok());
assert_eq!(TokenProgram::deserialize(&userdata), Ok(account));
let account = TokenProgram::Token(TokenInfo {
supply: 12345,
decimals: 2,
name: "A test token".to_string(),
symbol: "TEST".to_string(),
});
assert!(account.serialize(&mut userdata).is_ok());
assert_eq!(TokenProgram::deserialize(&userdata), Ok(account));
}
#[test]
pub fn serde_expect_fail() {
let mut userdata = vec![0; 256];
// Certain TokenProgram's may not be serialized
let account = TokenProgram::default();
assert_eq!(account, TokenProgram::Unallocated);
assert!(account.serialize(&mut userdata).is_err());
assert!(account.serialize(&mut userdata).is_err());
let account = TokenProgram::Invalid;
assert!(account.serialize(&mut userdata).is_err());
// Bad deserialize userdata
assert!(TokenProgram::deserialize(&[]).is_err());
assert!(TokenProgram::deserialize(&[1]).is_err());
assert!(TokenProgram::deserialize(&[1, 2]).is_err());
assert!(TokenProgram::deserialize(&[2, 2]).is_err());
assert!(TokenProgram::deserialize(&[3]).is_err());
}
// Note: business logic tests are located in the @solana/web3.js test suite
}

View File

@ -124,7 +124,7 @@ impl WriteStage {
} }
inc_new_counter_info!("write_stage-entries_received", num_new_entries); inc_new_counter_info!("write_stage-entries_received", num_new_entries);
info!("write_stage entries: {}", num_new_entries); debug!("write_stage entries: {}", num_new_entries);
let mut entries_send_total = 0; let mut entries_send_total = 0;
let mut crdt_votes_total = 0; let mut crdt_votes_total = 0;
@ -165,7 +165,7 @@ impl WriteStage {
"write_stage-time_ms", "write_stage-time_ms",
duration_as_ms(&now.elapsed()) as usize duration_as_ms(&now.elapsed()) as usize
); );
info!("done write_stage txs: {} time {} ms txs/s: {} entries_send_total: {} crdt_votes_total: {}", debug!("done write_stage txs: {} time {} ms txs/s: {} entries_send_total: {} crdt_votes_total: {}",
num_txs, duration_as_ms(&start.elapsed()), num_txs, duration_as_ms(&start.elapsed()),
num_txs as f32 / duration_as_s(&start.elapsed()), num_txs as f32 / duration_as_s(&start.elapsed()),
entries_send_total, entries_send_total,