Transaction format changes toward Credit-Only accounts (#4386)

* Add num_readonly_accounts slice

* Impl programs in account_keys

* Emulate current account-loading functionality using program-account_keys (breaks exchange_program_api tests)

* Fix test

* Add temporary exchange faucet id

* Update chacha golden

* Split num_credit_only_accounts into separate fields

* Improve readability

* Move message field constants into Message

* Add MessageHeader struct and fixup comments
This commit is contained in:
Tyera Eulberg 2019-05-22 18:23:16 -04:00 committed by GitHub
parent c121498b5b
commit 99d2428041
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 156 additions and 91 deletions

View File

@ -154,7 +154,7 @@ mod tests {
hasher.hash(&buf[..size]);
// golden needs to be updated if blob stuff changes....
let golden: Hash = "5Pz5KQyNht2nqkJhVd8F9zTFxzoDvbQSzaxQbtCPiyCo"
let golden: Hash = "9xb2Asf7UK5G8WqPwsvzo5xwLi4dixBSDiYKCtYRikA"
.parse()
.unwrap();

View File

@ -6,7 +6,9 @@
use crate::packet::{Packet, Packets};
use crate::result::Result;
use bincode::serialized_size;
use solana_metrics::inc_new_counter_debug;
use solana_sdk::message::MessageHeader;
use solana_sdk::pubkey::Pubkey;
use solana_sdk::short_vec::decode_len;
use solana_sdk::signature::Signature;
@ -16,9 +18,6 @@ use std::mem::size_of;
type TxOffsets = (Vec<u32>, Vec<u32>, Vec<u32>, Vec<u32>, Vec<Vec<u32>>);
// The serialized size of Message::num_required_signatures.
const NUM_REQUIRED_SIGNATURES_SIZE: usize = 1;
#[cfg(feature = "cuda")]
#[repr(C)]
struct Elems {
@ -116,7 +115,8 @@ pub fn get_packet_offsets(packet: &Packet, current_offset: u32) -> (u32, u32, u3
let sig_start = current_offset as usize + sig_size;
let msg_start = current_offset as usize + msg_start_offset;
let pubkey_start = msg_start + NUM_REQUIRED_SIGNATURES_SIZE + pubkey_size;
let pubkey_start =
msg_start + serialized_size(&MessageHeader::default()).unwrap() as usize + pubkey_size;
(
sig_len as u32,
@ -389,19 +389,19 @@ mod tests {
#[test]
fn test_get_packet_offsets() {
assert_eq!(get_packet_offsets_from_tx(test_tx(), 0), (1, 1, 64, 2));
assert_eq!(get_packet_offsets_from_tx(test_tx(), 100), (1, 1, 64, 2));
assert_eq!(get_packet_offsets_from_tx(test_tx(), 0), (1, 1, 64, 4));
assert_eq!(get_packet_offsets_from_tx(test_tx(), 100), (1, 1, 64, 4));
// Ensure we're not indexing packet by the `current_offset` parameter.
assert_eq!(
get_packet_offsets_from_tx(test_tx(), 1_000_000),
(1, 1, 64, 2)
(1, 1, 64, 4)
);
// Ensure we're returning sig_len, not sig_size.
assert_eq!(
get_packet_offsets_from_tx(test_multisig_tx(), 0),
(2, 1, 128, 2)
(2, 1, 128, 4)
);
}

View File

@ -382,7 +382,10 @@ mod tests {
let message = tx.message();
assert_eq!(tx.signatures.len(), 1);
assert_eq!(message.account_keys, vec![mint_pubkey, to]);
assert_eq!(
message.account_keys,
vec![mint_pubkey, to, Pubkey::default()]
);
assert_eq!(message.recent_blockhash, blockhash);
assert_eq!(message.instructions.len(), 1);

View File

@ -209,8 +209,9 @@ mod tests {
let mut message = Message::new(vec![instruction]);
// Attack! Part 2: Point the instruction to the expected, but unsigned, key.
message.account_keys.push(alice_pubkey);
message.account_keys.insert(3, alice_pubkey);
message.instructions[0].accounts[0] = 3;
message.instructions[0].program_ids_index = 4;
// Ensure the transaction fails because of the unsigned key.
assert_eq!(
@ -257,8 +258,9 @@ mod tests {
let mut message = Message::new(vec![instruction]);
// Attack! Part 2: Point the instruction to the expected, but unsigned, key.
message.account_keys.push(alice_pubkey);
message.account_keys.insert(3, alice_pubkey);
message.instructions[0].accounts[0] = 3;
message.instructions[0].program_ids_index = 4;
// Ensure the transaction fails because of the unsigned key.
assert_eq!(

View File

@ -2,7 +2,7 @@
use crate::exchange_instruction::*;
use crate::exchange_state::*;
use crate::id;
use crate::faucet_id;
use log::*;
use solana_metrics::inc_new_counter_info;
use solana_sdk::account::KeyedAccount;
@ -192,7 +192,7 @@ impl ExchangeProcessor {
let mut to_account =
Self::deserialize_account(&keyed_accounts[TO_ACCOUNT_INDEX].account.data)?;
if &id() == keyed_accounts[FROM_ACCOUNT_INDEX].unsigned_key() {
if &faucet_id() == keyed_accounts[FROM_ACCOUNT_INDEX].unsigned_key() {
to_account.tokens[token] += tokens;
} else {
let state: ExchangeState =
@ -460,7 +460,7 @@ pub fn process_instruction(
#[cfg(test)]
mod test {
use super::*;
use crate::exchange_instruction;
use crate::{exchange_instruction, id};
use solana_runtime::bank::Bank;
use solana_runtime::bank_client::BankClient;
use solana_sdk::client::SyncClient;
@ -588,8 +588,13 @@ mod test {
}
fn transfer(client: &BankClient, owner: &Keypair, to: &Pubkey, token: Token, tokens: u64) {
let instruction =
exchange_instruction::transfer_request(&owner.pubkey(), to, &id(), token, tokens);
let instruction = exchange_instruction::transfer_request(
&owner.pubkey(),
to,
&faucet_id(),
token,
tokens,
);
client
.send_instruction(owner, instruction)
.expect(&format!("{}:{}", line!(), file!()));
@ -664,8 +669,13 @@ mod test {
let new = create_token_account(&client, &owner);
let instruction =
exchange_instruction::transfer_request(&owner.pubkey(), &new, &id(), Token::A, 42);
let instruction = exchange_instruction::transfer_request(
&owner.pubkey(),
&new,
&faucet_id(),
Token::A,
42,
);
client
.send_instruction(&owner, instruction)
.expect(&format!("{}:{}", line!(), file!()));

View File

@ -11,3 +11,22 @@ pub const EXCHANGE_PROGRAM_ID: [u8; 32] = [
];
solana_sdk::solana_program_id!(EXCHANGE_PROGRAM_ID);
pub const EXCHANGE_FAUCET_ID: [u8; 32] = [
3, 147, 111, 103, 210, 47, 23, 11, 176, 29, 147, 89, 237, 155, 21, 62, 107, 105, 157, 1, 98,
204, 206, 211, 54, 212, 79, 15, 160, 0, 0, 0,
];
pub fn faucet_id() -> solana_sdk::pubkey::Pubkey {
solana_sdk::pubkey::Pubkey::new(&EXCHANGE_FAUCET_ID)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn exchange_faucet_id() {
let ids = [("ExchangeFaucet11111111111111111111111111111", faucet_id())];
assert!(ids.iter().all(|(name, id)| *name == id.to_string()));
}
}

View File

@ -180,11 +180,13 @@ impl Accounts {
// If a fee can pay for execution then the program will be scheduled
let mut called_accounts: Vec<Account> = vec![];
for key in &message.account_keys {
called_accounts.push(
AccountsDB::load(storage, ancestors, accounts_index, key)
.map(|(account, _)| account)
.unwrap_or_default(),
);
if !message.program_ids().contains(&key) {
called_accounts.push(
AccountsDB::load(storage, ancestors, accounts_index, key)
.map(|(account, _)| account)
.unwrap_or_default(),
);
}
}
if called_accounts.is_empty() || called_accounts[0].lamports == 0 {
error_counters.account_not_found += 1;
@ -255,11 +257,11 @@ impl Accounts {
.instructions
.iter()
.map(|ix| {
if message.program_ids().len() <= ix.program_ids_index as usize {
if message.account_keys.len() <= ix.program_ids_index as usize {
error_counters.account_not_found += 1;
return Err(TransactionError::AccountNotFound);
}
let program_id = message.program_ids()[ix.program_ids_index as usize];
let program_id = message.account_keys[ix.program_ids_index as usize];
Self::load_executable_accounts(
storage,
ancestors,
@ -469,9 +471,11 @@ impl Accounts {
let rv = txs
.iter()
.map(|tx| {
let message = tx.borrow().message();
Self::lock_account(
(&mut self.account_locks.lock().unwrap(), parent_record_locks),
&tx.borrow().message().account_keys,
&message.account_keys[..(message.account_keys.len()
- message.header.num_credit_only_unsigned_accounts as usize)],
&mut error_counters,
)
})
@ -493,7 +497,12 @@ impl Accounts {
{
let record_locks = self.record_locks.lock().unwrap();
for tx in txs {
Self::lock_record_account(&record_locks.0, &tx.borrow().message().account_keys);
let message = tx.borrow().message();
Self::lock_record_account(
&record_locks.0,
&message.account_keys[..(message.account_keys.len()
- message.header.num_credit_only_unsigned_accounts as usize)],
);
}
}
@ -684,7 +693,10 @@ mod tests {
assert_eq!(error_counters.account_not_found, 1);
assert_eq!(loaded_accounts.len(), 1);
assert_eq!(loaded_accounts[0], Err(TransactionError::AccountNotFound));
assert_eq!(
loaded_accounts[0],
Err(TransactionError::ProgramAccountNotFound)
);
}
#[test]
@ -736,7 +748,7 @@ mod tests {
let account = Account::new(2, 1, &Pubkey::default());
accounts.push((key1, account));
let instructions = vec![CompiledInstruction::new(0, &(), vec![0, 1])];
let instructions = vec![CompiledInstruction::new(2, &(), vec![0, 1])];
let tx = Transaction::new_with_compiled_instructions(
&[&keypair],
&[key1],
@ -807,7 +819,7 @@ mod tests {
account.owner = key5;
accounts.push((key6, account));
let instructions = vec![CompiledInstruction::new(0, &(), vec![0])];
let instructions = vec![CompiledInstruction::new(1, &(), vec![0])];
let tx = Transaction::new_with_compiled_instructions(
&[&keypair],
&[],
@ -918,8 +930,8 @@ mod tests {
accounts.push((key3, account));
let instructions = vec![
CompiledInstruction::new(0, &(), vec![0]),
CompiledInstruction::new(1, &(), vec![0]),
CompiledInstruction::new(2, &(), vec![0]),
];
let tx = Transaction::new_with_compiled_instructions(
&[&keypair],

View File

@ -125,7 +125,7 @@ impl MessageProcessor {
program_accounts: &mut [&mut Account],
tick_height: u64,
) -> Result<(), InstructionError> {
let program_id = instruction.program_id(message.program_ids());
let program_id = instruction.program_id(&message.account_keys);
let mut keyed_accounts = create_keyed_accounts(executable_accounts);
let mut keyed_accounts2: Vec<_> = instruction
@ -134,7 +134,7 @@ impl MessageProcessor {
.map(|&index| {
let index = index as usize;
let key = &message.account_keys[index];
(key, index < message.num_required_signatures as usize)
(key, index < message.header.num_required_signatures as usize)
})
.zip(program_accounts.iter_mut())
.map(|((key, is_signer), account)| KeyedAccount::new(key, is_signer, account))
@ -173,7 +173,7 @@ impl MessageProcessor {
program_accounts: &mut [&mut Account],
tick_height: u64,
) -> Result<(), InstructionError> {
let program_id = instruction.program_id(message.program_ids());
let program_id = instruction.program_id(&message.account_keys);
// TODO: the runtime should be checking read/write access to memory
// we are trusting the hard-coded programs not to clobber or allocate
let pre_total: u64 = program_accounts.iter().map(|a| a.lamports).sum();
@ -221,7 +221,8 @@ impl MessageProcessor {
tick_height: u64,
) -> Result<(), TransactionError> {
for (instruction_index, instruction) in message.instructions.iter().enumerate() {
let executable_accounts = &mut loaders[instruction.program_ids_index as usize];
let executable_accounts = &mut loaders
[message.program_index_in_program_ids(instruction.program_ids_index) as usize];
let mut program_accounts = get_subset_unchecked_mut(accounts, &instruction.accounts)
.map_err(|err| TransactionError::InstructionError(instruction_index as u8, err))?;
self.execute_instruction(

View File

@ -14,7 +14,7 @@ impl FeeCalculator {
}
pub fn calculate_fee(&self, message: &Message) -> u64 {
self.lamports_per_signature * u64::from(message.num_required_signatures)
self.lamports_per_signature * u64::from(message.header.num_required_signatures)
}
}

View File

@ -107,7 +107,7 @@ impl AccountMeta {
/// An instruction to execute a program
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct CompiledInstruction {
/// Index into the transaction program ids array indicating the program account that executes this instruction
/// Index into the transaction keys array indicating the program account that executes this instruction
pub program_ids_index: u8,
/// Ordered indices into the transaction keys array indicating which accounts to pass to the program
#[serde(with = "short_vec")]

View File

@ -10,11 +10,7 @@ fn position(keys: &[Pubkey], key: &Pubkey) -> u8 {
keys.iter().position(|k| k == key).unwrap() as u8
}
fn compile_instruction(
ix: Instruction,
keys: &[Pubkey],
program_ids: &[Pubkey],
) -> CompiledInstruction {
fn compile_instruction(ix: Instruction, keys: &[Pubkey]) -> CompiledInstruction {
let accounts: Vec<_> = ix
.accounts
.iter()
@ -22,19 +18,15 @@ fn compile_instruction(
.collect();
CompiledInstruction {
program_ids_index: position(program_ids, &ix.program_ids_index),
program_ids_index: position(keys, &ix.program_ids_index),
data: ix.data.clone(),
accounts,
}
}
fn compile_instructions(
ixs: Vec<Instruction>,
keys: &[Pubkey],
program_ids: &[Pubkey],
) -> Vec<CompiledInstruction> {
fn compile_instructions(ixs: Vec<Instruction>, keys: &[Pubkey]) -> Vec<CompiledInstruction> {
ixs.into_iter()
.map(|ix| compile_instruction(ix, keys, program_ids))
.map(|ix| compile_instruction(ix, keys))
.collect()
}
@ -78,12 +70,27 @@ fn get_program_ids(instructions: &[Instruction]) -> Vec<Pubkey> {
.collect()
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct Message {
#[derive(Serialize, Deserialize, Default, Debug, PartialEq, Eq, Clone)]
pub struct MessageHeader {
/// The number of signatures required for this message to be considered valid. The
/// signatures must match the first `num_required_signatures` of `account_keys`.
pub num_required_signatures: u8,
/// The last num_credit_only_signed_accounts of the signed keys are credit-only accounts.
/// Programs may process multiple transactions that add lamports to the same credit-only
/// account within a single PoH entry, but are not permitted to debit lamports or modify
/// account data. Transactions targeting the same debit account are evaluated sequentially.
pub num_credit_only_signed_accounts: u8,
/// The last num_credit_only_unsigned_accounts of the unsigned keys are credit-only accounts.
pub num_credit_only_unsigned_accounts: u8,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct Message {
/// The message header, identifying signed and credit-only `account_keys`
pub header: MessageHeader,
/// All the account keys used by this transaction
#[serde(with = "short_vec")]
pub account_keys: Vec<Pubkey>,
@ -91,10 +98,6 @@ pub struct Message {
/// The id of a recent ledger entry.
pub recent_blockhash: Hash,
/// All the program id keys used to execute this transaction's instructions
#[serde(with = "short_vec")]
program_ids: Vec<Pubkey>,
/// Programs that will be executed in sequence and committed in one atomic transaction if all
/// succeed.
#[serde(with = "short_vec")]
@ -104,16 +107,20 @@ pub struct Message {
impl Message {
pub fn new_with_compiled_instructions(
num_required_signatures: u8,
num_credit_only_signed_accounts: u8,
num_credit_only_unsigned_accounts: u8,
account_keys: Vec<Pubkey>,
recent_blockhash: Hash,
program_ids: Vec<Pubkey>,
instructions: Vec<CompiledInstruction>,
) -> Self {
Self {
num_required_signatures,
header: MessageHeader {
num_required_signatures,
num_credit_only_signed_accounts,
num_credit_only_unsigned_accounts,
},
account_keys,
recent_blockhash,
program_ids,
instructions,
}
}
@ -126,19 +133,28 @@ impl Message {
let program_ids = get_program_ids(&instructions);
let (mut signed_keys, unsigned_keys) = get_keys(&instructions, payer);
let num_required_signatures = signed_keys.len() as u8;
let num_credit_only_signed_accounts = 0;
let num_credit_only_unsigned_accounts = program_ids.len() as u8;
signed_keys.extend(&unsigned_keys);
let instructions = compile_instructions(instructions, &signed_keys, &program_ids);
signed_keys.extend(&program_ids);
let instructions = compile_instructions(instructions, &signed_keys);
Self::new_with_compiled_instructions(
num_required_signatures,
num_credit_only_signed_accounts,
num_credit_only_unsigned_accounts,
signed_keys,
Hash::default(),
program_ids,
instructions,
)
}
pub fn program_ids(&self) -> &[Pubkey] {
&self.program_ids
&self.account_keys
[self.account_keys.len() - self.header.num_credit_only_unsigned_accounts as usize..]
}
pub fn program_index_in_program_ids(&self, index: u8) -> u8 {
index - (self.account_keys.len() as u8 - self.header.num_credit_only_unsigned_accounts)
}
}
@ -293,16 +309,16 @@ mod tests {
let id0 = Pubkey::default();
let ix = Instruction::new(program_id, &0, vec![AccountMeta::new(id0, false)]);
let message = Message::new(vec![ix]);
assert_eq!(message.num_required_signatures, 0);
assert_eq!(message.header.num_required_signatures, 0);
let ix = Instruction::new(program_id, &0, vec![AccountMeta::new(id0, true)]);
let message = Message::new(vec![ix]);
assert_eq!(message.num_required_signatures, 1);
assert_eq!(message.header.num_required_signatures, 1);
}
#[test]
fn test_message_kitchen_sink() {
let program_id0 = Pubkey::default();
let program_id0 = Pubkey::new_rand();
let program_id1 = Pubkey::new_rand();
let id0 = Pubkey::default();
let keypair1 = Keypair::new();
@ -314,15 +330,15 @@ mod tests {
]);
assert_eq!(
message.instructions[0],
CompiledInstruction::new(0, &0, vec![1])
CompiledInstruction::new(2, &0, vec![1])
);
assert_eq!(
message.instructions[1],
CompiledInstruction::new(1, &0, vec![0])
CompiledInstruction::new(3, &0, vec![0])
);
assert_eq!(
message.instructions[2],
CompiledInstruction::new(0, &0, vec![0])
CompiledInstruction::new(2, &0, vec![0])
);
}
@ -334,11 +350,11 @@ mod tests {
let ix = Instruction::new(program_id, &0, vec![AccountMeta::new(id0, false)]);
let message = Message::new_with_payer(vec![ix], Some(&payer));
assert_eq!(message.num_required_signatures, 1);
assert_eq!(message.header.num_required_signatures, 1);
let ix = Instruction::new(program_id, &0, vec![AccountMeta::new(id0, true)]);
let message = Message::new_with_payer(vec![ix], Some(&payer));
assert_eq!(message.num_required_signatures, 2);
assert_eq!(message.header.num_required_signatures, 2);
let ix = Instruction::new(
program_id,
@ -346,7 +362,7 @@ mod tests {
vec![AccountMeta::new(payer, true), AccountMeta::new(id0, true)],
);
let message = Message::new_with_payer(vec![ix], Some(&payer));
assert_eq!(message.num_required_signatures, 2);
assert_eq!(message.header.num_required_signatures, 2);
}
}

View File

@ -67,7 +67,7 @@ pub struct Transaction {
impl Transaction {
pub fn new_unsigned(message: Message) -> Self {
Self {
signatures: vec![Signature::default(); message.num_required_signatures as usize],
signatures: vec![Signature::default(); message.header.num_required_signatures as usize],
message,
}
}
@ -115,11 +115,13 @@ impl Transaction {
.map(|keypair| (*keypair).pubkey())
.collect();
account_keys.extend_from_slice(keys);
account_keys.extend(&program_ids);
let message = Message::new_with_compiled_instructions(
from_keypairs.len() as u8,
0,
program_ids.len() as u8,
account_keys,
Hash::default(),
program_ids,
instructions,
);
Transaction::new(from_keypairs, message, recent_blockhash)
@ -175,7 +177,7 @@ impl Transaction {
/// Check keys and keypair lengths, then sign this transaction.
pub fn sign<T: KeypairUtil>(&mut self, keypairs: &[&T], recent_blockhash: Hash) {
let signed_keys =
&self.message.account_keys[0..self.message.num_required_signatures as usize];
&self.message.account_keys[0..self.message.header.num_required_signatures as usize];
for (i, keypair) in keypairs.iter().enumerate() {
assert_eq!(keypair.pubkey(), signed_keys[i], "keypair-pubkey mismatch");
}
@ -188,7 +190,7 @@ impl Transaction {
/// clear any prior signatures and update recent_blockhash
pub fn partial_sign<T: KeypairUtil>(&mut self, keypairs: &[&T], recent_blockhash: Hash) {
let signed_keys =
&self.message.account_keys[0..self.message.num_required_signatures as usize];
&self.message.account_keys[0..self.message.header.num_required_signatures as usize];
// if you change the blockhash, you're re-signing...
if recent_blockhash != self.message.recent_blockhash {
@ -218,7 +220,7 @@ impl Transaction {
pub fn verify_refs(&self) -> bool {
let message = self.message();
for instruction in &message.instructions {
if (instruction.program_ids_index as usize) >= message.program_ids().len() {
if (instruction.program_ids_index as usize) >= message.account_keys.len() {
return false;
}
for account_index in &instruction.accounts {
@ -304,7 +306,7 @@ mod tests {
#[test]
fn test_refs_invalid_account() {
let key = Keypair::new();
let instructions = vec![CompiledInstruction::new(0, &(), vec![1])];
let instructions = vec![CompiledInstruction::new(0, &(), vec![2])];
let tx = Transaction::new_with_compiled_instructions(
&[&key],
&[],
@ -380,18 +382,18 @@ mod tests {
let len_size = 1;
let num_required_sigs_size = 1;
let num_credit_only_accounts_size = 2;
let blockhash_size = size_of::<Hash>();
let expected_transaction_size = len_size
+ (tx.signatures.len() * size_of::<Signature>())
+ num_required_sigs_size
+ num_credit_only_accounts_size
+ len_size
+ (tx.message.account_keys.len() * size_of::<Pubkey>())
+ blockhash_size
+ len_size
+ (tx.message.program_ids().len() * size_of::<Pubkey>())
+ len_size
+ expected_instruction_size;
assert_eq!(expected_transaction_size, 214);
assert_eq!(expected_transaction_size, 215);
assert_eq!(
serialized_size(&tx).unwrap() as usize,
@ -407,16 +409,16 @@ mod tests {
assert_eq!(
serialize(&create_sample_transaction()).unwrap(),
vec![
1, 134, 84, 186, 62, 126, 175, 48, 6, 80, 185, 139, 108, 109, 157, 213, 17, 249, 3,
79, 83, 21, 89, 242, 148, 51, 140, 115, 77, 161, 134, 116, 136, 206, 171, 239, 236,
240, 19, 73, 217, 152, 60, 159, 170, 41, 104, 29, 217, 93, 65, 139, 191, 202, 181,
77, 246, 26, 15, 156, 186, 66, 32, 139, 6, 1, 2, 156, 227, 116, 193, 215, 38, 142,
22, 8, 14, 229, 239, 119, 93, 5, 218, 161, 35, 3, 33, 0, 36, 100, 158, 252, 33,
161, 97, 185, 62, 89, 99, 1, 1, 1, 4, 5, 6, 7, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 8, 7, 6, 5, 4, 1, 1, 1, 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, 0, 1, 2, 2, 2, 4, 5, 6, 7, 8, 9, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9, 8, 7, 6, 5, 4, 2, 2, 2, 1, 0, 2, 0, 1, 3,
1, 2, 3
1, 71, 59, 9, 187, 190, 129, 150, 165, 21, 33, 158, 72, 87, 110, 144, 120, 79, 238,
132, 134, 105, 39, 102, 116, 209, 29, 229, 154, 36, 105, 44, 172, 118, 131, 22,
124, 131, 179, 142, 176, 27, 117, 160, 89, 102, 224, 204, 1, 252, 141, 2, 136, 0,
37, 218, 225, 129, 92, 154, 250, 59, 97, 178, 10, 1, 0, 1, 3, 156, 227, 116, 193,
215, 38, 142, 22, 8, 14, 229, 239, 119, 93, 5, 218, 161, 35, 3, 33, 0, 36, 100,
158, 252, 33, 161, 97, 185, 62, 89, 99, 1, 1, 1, 4, 5, 6, 7, 8, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 8, 7, 6, 5, 4, 1, 1, 1, 2, 2, 2, 4, 5, 6, 7, 8, 9, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9, 8, 7, 6, 5, 4, 2, 2, 2, 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, 0, 1, 2,
2, 0, 1, 3, 1, 2, 3
]
);
}
@ -498,7 +500,7 @@ mod tests {
tx.sign(&[&keypair0], Hash::default());
assert_eq!(
tx.message.instructions[0],
CompiledInstruction::new(0, &0, vec![0])
CompiledInstruction::new(1, &0, vec![0])
);
assert!(tx.is_signed());
}