flow_rust_sdk/
lib.rs

1//! Flow-Rust-SDK API Reference
2//!
3//! See the [Wiki](https://github.com/MarshallBelles/flow-rust-sdk/wiki) for usage instructions.
4
5// ****************************************************
6// License: Apache V2.0 OR MIT, at your option
7// ****************************************************
8
9// ****************************************************
10// External Dependencies
11// ****************************************************
12use std::error;
13
14use flow::access_api_client::AccessApiClient;
15
16use flow::*;
17
18pub mod flow {
19    //! `flow` is an exported module from the flow_rust_sdk.
20    //! It's types are generated directly from the gRPC API Protobufs
21    //! https://github.com/onflow/flow/tree/master/protobuf
22    tonic::include_proto!("flow.access");
23}
24
25// for signing transactions
26use bytes::Bytes;
27pub use p256_flow::ecdsa::SigningKey;
28use p256_flow::ecdsa::{signature_flow::Signature, signature_flow::Signer};
29use p256_flow::elliptic_curve_flow::SecretKey;
30pub use rand_core::OsRng;
31pub extern crate hex;
32pub extern crate rlp;
33use rlp::*;
34
35// ****************************************************
36// Connection Object
37// ****************************************************
38
39/// The FlowConnection object contains a single API connection.
40/// The network transport layer can be optionally substitued by implementing a new FlowConnection<T>
41pub struct FlowConnection<T> {
42    pub client: AccessApiClient<T>,
43}
44
45/// The default implementation of a FlowConnection, using `tonic::transport::Channel`
46impl FlowConnection<tonic::transport::Channel> {
47    /// Initializes a new connection and checks the availability of the node at the provided address
48    pub async fn new(
49        network_address: &str,
50    ) -> Result<FlowConnection<tonic::transport::Channel>, Box<dyn error::Error>> {
51        let mut client = AccessApiClient::connect(network_address.to_owned()).await?;
52        let request = tonic::Request::new(PingRequest {});
53        client.ping(request).await?;
54        Ok(FlowConnection::<tonic::transport::Channel> { client })
55    }
56    /// get_account will return the `flow::AccountResponse` of `account_address`, else an error if it could not be accessed.
57    pub async fn get_account(
58        &mut self,
59        account_address: &str,
60    ) -> Result<AccountResponse, Box<dyn error::Error>> {
61        let request = tonic::Request::new(GetAccountAtLatestBlockRequest {
62            address: hex::decode(account_address).unwrap(),
63        });
64        let response = self.client.get_account_at_latest_block(request).await?;
65        Ok(response.into_inner())
66    }
67    /// execute_script will attempt to run the provided script (as bytes) and return the `flow::ExecuteScriptResponse` or Error
68    pub async fn execute_script(
69        &mut self,
70        script: Vec<u8>,
71        arguments: Vec<Vec<u8>>,
72        block_height: Option<u64>,
73        block_id: Option<Vec<u8>>,
74    ) -> Result<ExecuteScriptResponse, Box<dyn error::Error>> {
75        if block_id.is_some() {
76            // we are running the script against a specific block
77            let request = tonic::Request::new(ExecuteScriptAtBlockIdRequest {
78                script,
79                arguments,
80                block_id: block_id.unwrap(),
81            });
82            let response = self.client.execute_script_at_block_id(request).await?;
83            Ok(response.into_inner())
84        } else if block_height.is_some() {
85            // we are running the script against a block height
86            let request = tonic::Request::new(ExecuteScriptAtBlockHeightRequest {
87                script,
88                arguments,
89                block_height: block_height.unwrap(),
90            });
91            let response = self.client.execute_script_at_block_height(request).await?;
92            Ok(response.into_inner())
93        } else {
94            let request =
95                tonic::Request::new(ExecuteScriptAtLatestBlockRequest { script, arguments });
96            let response = self.client.execute_script_at_latest_block(request).await?;
97            Ok(response.into_inner())
98        }
99    }
100    /// Sends the transaction to the blockchain.
101    /// Make sure you signed the transactionsign_transaction first.
102    pub async fn send_transaction(
103        &mut self,
104        transaction: Option<Transaction>,
105    ) -> Result<SendTransactionResponse, Box<dyn error::Error>> {
106        // send to blockchain
107        let request = tonic::Request::new(SendTransactionRequest { transaction });
108        let response = self.client.send_transaction(request).await?;
109        Ok(response.into_inner())
110    }
111    /// get transaction result
112    pub async fn get_transaction_result(
113        &mut self,
114        id: Vec<u8>,
115    ) -> Result<TransactionResultResponse, Box<dyn error::Error>> {
116        // send to blockchain
117        let request = tonic::Request::new(GetTransactionRequest { id });
118        let response = self.client.get_transaction_result(request).await?;
119        Ok(response.into_inner())
120    }
121    /// get_block accepts either the block_id or block_height. If neither are defined it returns the latest block.
122    pub async fn get_block(
123        &mut self,
124        block_id: Option<String>,
125        block_height: Option<u64>,
126        is_sealed: Option<bool>,
127    ) -> Result<BlockResponse, Box<dyn error::Error>> {
128        if block_id.is_some() {
129            // IF block_id, use this
130            let request = tonic::Request::new(GetBlockByIdRequest {
131                id: hex::decode(block_id.unwrap())?,
132            });
133            let response = self.client.get_block_by_id(request).await?;
134            Ok(response.into_inner())
135        } else if block_height.is_some() {
136            // else IF block_height, use that
137            let request = tonic::Request::new(GetBlockByHeightRequest {
138                height: block_height.unwrap(),
139            });
140            let response = self.client.get_block_by_height(request).await?;
141            Ok(response.into_inner())
142        } else {
143            // else, just get latest block
144            if is_sealed.is_some() {
145                let request = tonic::Request::new(GetLatestBlockRequest {
146                    is_sealed: is_sealed.unwrap(),
147                });
148                let response = self.client.get_latest_block(request).await?;
149                Ok(response.into_inner())
150            } else {
151                let request = tonic::Request::new(GetLatestBlockRequest { is_sealed: false });
152                let response = self.client.get_latest_block(request).await?;
153                Ok(response.into_inner())
154            }
155        }
156    }
157    /// retrieve the specified events by type for the given height range
158    pub async fn get_events_for_height_range(
159        &mut self,
160        event_type: &str,
161        start_height: u64,
162        end_height: u64,
163    ) -> Result<EventsResponse, Box<dyn error::Error>> {
164        let request = tonic::Request::new(GetEventsForHeightRangeRequest {
165            r#type: event_type.to_owned(),
166            start_height,
167            end_height,
168        });
169        let response = self.client.get_events_for_height_range(request).await?;
170        Ok(response.into_inner())
171    }
172    /// retrieve the specified events by type for the given blocks
173    pub async fn get_events_for_block_ids(
174        &mut self,
175        event_type: &str,
176        ids: Vec<Vec<u8>>,
177    ) -> Result<EventsResponse, Box<dyn error::Error>> {
178        let request = tonic::Request::new(GetEventsForBlockIdsRequest {
179            r#type: event_type.to_owned(),
180            block_ids: ids,
181        });
182        let response = self.client.get_events_for_block_i_ds(request).await?;
183        Ok(response.into_inner())
184    }
185    /// retrieve the specified collections
186    pub async fn get_collection(
187        &mut self,
188        collection_id: Vec<u8>,
189    ) -> Result<CollectionResponse, Box<dyn error::Error>> {
190        let request = tonic::Request::new(GetCollectionByIdRequest { id: collection_id });
191        let response = self.client.get_collection_by_id(request).await?;
192        Ok(response.into_inner())
193    }
194    /// Create an account with the given `account_keys` and `payer`
195    pub async fn create_account(
196        &mut self,
197        account_keys: Vec<String>,
198        payer: &str,
199        payer_private_key: &str,
200        key_id: u32,
201    ) -> Result<flow::Account, Box<dyn error::Error>> {
202        let create_account_template = b"
203        import Crypto
204
205        transaction(publicKeys: [String], contracts: {String: String}) {
206            prepare(signer: AuthAccount) {
207                let acct = AuthAccount(payer: signer)
208        
209                for pkey in publicKeys {
210                    let key = PublicKey(
211                        publicKey: pkey.decodeHex(),
212                        signatureAlgorithm: SignatureAlgorithm.ECDSA_P256
213                    )
214			              acct.keys.add(publicKey: key, hashAlgorithm: HashAlgorithm.SHA3_256, weight: 1000.0)
215                }
216        
217                for contract in contracts.keys {
218                    acct.contracts.add(name: contract, code: contracts[contract]!.decodeHex())
219                }
220            }
221        }";
222
223        let latest_block: BlockResponse = self.get_block(None, None, Some(false)).await?;
224        let account: flow::Account = self.get_account(payer).await?.account.unwrap();
225        let proposer = TransactionProposalKey {
226            address: hex::decode(payer).unwrap(),
227            key_id,
228            sequence_number: account.keys[key_id as usize].sequence_number as u64,
229        };
230        let keys_arg = process_keys_args(account_keys);
231        // empty contracts for now - will implement in the future
232        let contracts_arg = Argument::dictionary(vec![]);
233        let keys_arg = json!(keys_arg);
234        let contracts_arg = json!(contracts_arg);
235        let transaction: Transaction = build_transaction(
236            create_account_template.to_vec(),
237            vec![to_vec(&keys_arg)?, to_vec(&contracts_arg)?],
238            latest_block.block.unwrap().id,
239            1000,
240            proposer,
241            vec![payer.to_owned()],
242            payer.to_owned(),
243        )
244        .await?;
245        let signature = Sign {
246            address: payer.to_owned(),
247            key_id,
248            private_key: payer_private_key.to_owned(),
249        };
250        let transaction: Option<Transaction> =
251            sign_transaction(transaction, vec![], vec![&signature]).await?;
252        let transaction: SendTransactionResponse = self.send_transaction(transaction).await?;
253        // poll for transaction completion
254        let mut time: u64 = 50;
255        let mut i = 0;
256        println!("{}", hex::encode(transaction.id.to_vec()));
257        while i < 50 {
258            i += 1;
259            sleep(Duration::from_millis(time)).await;
260            let res = self.get_transaction_result(transaction.id.to_vec()).await?;
261            match res.status {
262                0 | 1 | 2 | 3 => {
263                    time += 200;
264                }
265                4 => {
266                    if res.status_code == 1 {
267                        // stop execution, error.
268                        return Err("Error during execution".into());
269                    }
270                    let new_account_address: flow::Event = res
271                        .events
272                        .into_iter()
273                        .filter(|x| x.r#type == "flow.AccountCreated")
274                        .collect::<Vec<flow::Event>>()
275                        .pop()
276                        .unwrap();
277                    let payload: Value = from_slice(&new_account_address.payload)?;
278                    let address: String = payload["value"]["fields"][0]["value"]["value"]
279                        .to_string()
280                        .split_at(3)
281                        .1
282                        .to_string()
283                        .split_at(16)
284                        .0
285                        .to_string();
286                    let acct: flow::Account = self
287                        .get_account(&address)
288                        .await?
289                        .account
290                        .expect("could not get newly created account");
291                    return Ok(acct);
292                }
293                _ => return Err("Cadence Runtime Error".into()),
294            }
295        }
296        Err("Could not produce result".into())
297    }
298    /// add a key
299    pub async fn add_key(
300        &mut self,
301        public_key_to_add: &str,
302        payer: &str,
303        payer_private_key: &str,
304        key_id: u32,
305    ) -> Result<flow::SendTransactionResponse, Box<dyn error::Error>> {
306        let update_contract_template = b"
307        import Crypto
308
309        transaction(publicKey: String) {
310            prepare(signer: AuthAccount) {
311                    let key = PublicKey(
312                        publicKey: pkey.decodeHex(),
313                        signatureAlgorithm: SignatureAlgorithm.ECDSA_P256
314                    )
315                    signer.keys.add(publicKey: key, hashAlgorithm: HashAlgorithm.SHA3_256, weight: 1000.0)
316            }
317        }
318        ";
319        let latest_block: BlockResponse = self.get_block(None, None, Some(false)).await?;
320        let account: flow::Account = self.get_account(payer).await?.account.unwrap();
321        let proposer = TransactionProposalKey {
322            address: hex::decode(payer).unwrap(),
323            key_id,
324            sequence_number: account.keys[key_id as usize].sequence_number as u64,
325        };
326        let public_key_to_add_arg = Argument::str(public_key_to_add);
327        let transaction: Transaction = build_transaction(
328            update_contract_template.to_vec(),
329            vec![public_key_to_add_arg.encode_str()],
330            latest_block.block.unwrap().id,
331            1000,
332            proposer,
333            vec![payer.to_owned()],
334            payer.to_owned(),
335        )
336        .await?;
337        let signature = Sign {
338            address: payer.to_owned(),
339            key_id,
340            private_key: payer_private_key.to_owned(),
341        };
342        let transaction: Option<Transaction> =
343            sign_transaction(transaction, vec![], vec![&signature]).await?;
344        let transaction: SendTransactionResponse = self.send_transaction(transaction).await?;
345
346        Ok(transaction)
347    }
348    /// remove a key
349    pub async fn remove_key(
350        &mut self,
351        key_to_remove: u64,
352        payer: &str,
353        payer_private_key: &str,
354        key_id: u32,
355    ) -> Result<flow::SendTransactionResponse, Box<dyn error::Error>> {
356        let update_contract_template = b"
357        transaction(keyIndex: Int) {
358            prepare(signer: AuthAccount) {
359                signer.keys.revoke(keyIndex)
360            }
361        }
362        ";
363        let latest_block: BlockResponse = self.get_block(None, None, Some(false)).await?;
364        let account: flow::Account = self.get_account(payer).await?.account.unwrap();
365        let proposer = TransactionProposalKey {
366            address: hex::decode(payer).unwrap(),
367            key_id,
368            sequence_number: account.keys[key_id as usize].sequence_number as u64,
369        };
370        let key_to_remove_arg = Argument::uint64(key_to_remove);
371        let transaction: Transaction = build_transaction(
372            update_contract_template.to_vec(),
373            vec![key_to_remove_arg.encode()],
374            latest_block.block.unwrap().id,
375            1000,
376            proposer,
377            vec![payer.to_owned()],
378            payer.to_owned(),
379        )
380        .await?;
381        let signature = Sign {
382            address: payer.to_owned(),
383            key_id,
384            private_key: payer_private_key.to_owned(),
385        };
386        let transaction: Option<Transaction> =
387            sign_transaction(transaction, vec![], vec![&signature]).await?;
388        let transaction: SendTransactionResponse = self.send_transaction(transaction).await?;
389
390        Ok(transaction)
391    }
392    /// add a contract
393    pub async fn add_contract(
394        &mut self,
395        contract_name: &str,
396        contract_code: &str,
397        payer: &str,
398        payer_private_key: &str,
399        key_id: u32,
400    ) -> Result<flow::SendTransactionResponse, Box<dyn error::Error>> {
401        let update_contract_template = b"
402        transaction(name: String, code: String) {
403            prepare(signer: AuthAccount) {
404                signer.contracts.add(name: name, code: code.decodeHex())
405            }
406        }
407        ";
408        let latest_block: BlockResponse = self.get_block(None, None, Some(false)).await?;
409        let account: flow::Account = self.get_account(payer).await?.account.unwrap();
410        let proposer = TransactionProposalKey {
411            address: hex::decode(payer).unwrap(),
412            key_id,
413            sequence_number: account.keys[key_id as usize].sequence_number as u64,
414        };
415        let contract_name_arg = Argument::str(contract_name);
416        let contract_code_arg = Argument::str(contract_code);
417        let transaction: Transaction = build_transaction(
418            update_contract_template.to_vec(),
419            vec![
420                contract_name_arg.encode_str(),
421                contract_code_arg.encode_str(),
422            ],
423            latest_block.block.unwrap().id,
424            1000,
425            proposer,
426            vec![payer.to_owned()],
427            payer.to_owned(),
428        )
429        .await?;
430        let signature = Sign {
431            address: payer.to_owned(),
432            key_id,
433            private_key: payer_private_key.to_owned(),
434        };
435        let transaction: Option<Transaction> =
436            sign_transaction(transaction, vec![], vec![&signature]).await?;
437        let transaction: SendTransactionResponse = self.send_transaction(transaction).await?;
438
439        Ok(transaction)
440    }
441    /// update a contract
442    pub async fn update_contract(
443        &mut self,
444        contract_name: &str,
445        contract_code: &str,
446        payer: &str,
447        payer_private_key: &str,
448        key_id: u32,
449    ) -> Result<flow::SendTransactionResponse, Box<dyn error::Error>> {
450        let update_contract_template = b"
451        transaction(name: String, code: String) {
452            prepare(signer: AuthAccount) {
453                signer.contracts.update__experimental(name: name, code: code.decodeHex())
454            }
455        }
456        ";
457        let latest_block: BlockResponse = self.get_block(None, None, Some(false)).await?;
458        let account: flow::Account = self.get_account(payer).await?.account.unwrap();
459        let proposer = TransactionProposalKey {
460            address: hex::decode(payer).unwrap(),
461            key_id,
462            sequence_number: account.keys[key_id as usize].sequence_number as u64,
463        };
464        let contract_name_arg = Argument::str(contract_name);
465        let contract_code_arg = Argument::str(contract_code);
466        let transaction: Transaction = build_transaction(
467            update_contract_template.to_vec(),
468            vec![
469                contract_name_arg.encode_str(),
470                contract_code_arg.encode_str(),
471            ],
472            latest_block.block.unwrap().id,
473            1000,
474            proposer,
475            vec![payer.to_owned()],
476            payer.to_owned(),
477        )
478        .await?;
479        let signature = Sign {
480            address: payer.to_owned(),
481            key_id,
482            private_key: payer_private_key.to_owned(),
483        };
484        let transaction: Option<Transaction> =
485            sign_transaction(transaction, vec![], vec![&signature]).await?;
486        let transaction: SendTransactionResponse = self.send_transaction(transaction).await?;
487
488        Ok(transaction)
489    }
490    /// remove a contract
491    pub async fn remove_contract(
492        &mut self,
493        contract_name: &str,
494        payer: &str,
495        payer_private_key: &str,
496        key_id: u32,
497    ) -> Result<flow::SendTransactionResponse, Box<dyn error::Error>> {
498        let update_contract_template = b"
499        transaction(name: String) {
500            prepare(signer: AuthAccount) {
501                signer.contracts.remove(name: name)
502            }
503        }
504        ";
505        let latest_block: BlockResponse = self.get_block(None, None, Some(false)).await?;
506        let account: flow::Account = self.get_account(payer).await?.account.unwrap();
507        let proposer = TransactionProposalKey {
508            address: hex::decode(payer).unwrap(),
509            key_id,
510            sequence_number: account.keys[key_id as usize].sequence_number as u64,
511        };
512        let contract_name_arg = Argument::str(contract_name);
513        let transaction: Transaction = build_transaction(
514            update_contract_template.to_vec(),
515            vec![contract_name_arg.encode_str()],
516            latest_block.block.unwrap().id,
517            1000,
518            proposer,
519            vec![payer.to_owned()],
520            payer.to_owned(),
521        )
522        .await?;
523        let signature = Sign {
524            address: payer.to_owned(),
525            key_id,
526            private_key: payer_private_key.to_owned(),
527        };
528        let transaction: Option<Transaction> =
529            sign_transaction(transaction, vec![], vec![&signature]).await?;
530        let transaction: SendTransactionResponse = self.send_transaction(transaction).await?;
531
532        Ok(transaction)
533    }
534}
535
536// ****************************************************
537// Utility Functionality
538// ****************************************************
539
540use serde::Serialize;
541pub use serde_json::{from_slice, json, to_vec, Value};
542use tokio::time::{sleep, Duration};
543
544/// This is our argument builder.
545#[derive(Serialize)]
546pub struct Argument<T> {
547    r#type: &'static str,
548    value: T,
549}
550/// Argument builder assuming a vec<String>
551impl Argument<Vec<Value>> {
552    /// Argument from array
553    pub fn array(values: Vec<Value>) -> Argument<Vec<Value>> {
554        Argument {
555            r#type: "Array",
556            value: values,
557        }
558    }
559    /// Argument from dictionary `Vec<(String, String)>`
560    pub fn dictionary(values: Vec<(String, String)>) -> Argument<Vec<Value>> {
561        Argument {
562            r#type: "Dictionary",
563            value: values
564                .into_iter()
565                .map(|(x, y)| json!({"Key":x, "Value":y}))
566                .collect(),
567        }
568    }
569    // process and encode bytes argument
570    pub fn encode_arr(&self) -> Vec<u8> {
571        to_vec(&json!(self)).unwrap()
572    }
573}
574/// Boolean arguments
575impl Argument<bool> {
576    pub fn boolean(value: bool) -> Argument<bool> {
577        Argument {
578            r#type: "Bool",
579            value,
580        }
581    }
582}
583/// You can use this to avoid memory allocation when dealing only with str
584impl Argument<&str> {
585    pub fn str(value: &str) -> Argument<&str> {
586        Argument {
587            r#type: "String",
588            value,
589        }
590    }
591    // process and encode bytes argument. Using this instead of `encode()` bypasses memory allocation as we don't have to worry about `String`s
592    pub fn encode_str(&self) -> Vec<u8> {
593        to_vec(&json!(self)).unwrap()
594    }
595}
596/// You will use this for most argument types. Before implementing new types, be sure to read https://docs.onflow.org/cadence/json-cadence-spec
597impl Argument<String> {
598    /// Take a String and turn it into an argument
599    pub fn string(value: String) -> Argument<String> {
600        Argument {
601            r#type: "String",
602            value,
603        }
604    }
605    /// Take a positive f64 and turn it into an argument. Fixed point numbers are encoded as strings, so this will result in additional memory allocation when used.
606    pub fn ufix64(value: f64) -> Argument<String> {
607        assert!(value >= 0.0, "{}", true); // cannot have a negative ufix
608        Argument {
609            r#type: "UFix64",
610            value: value.to_string(),
611        }
612    }
613    /// Take a f64 and turn it into an argument. Fixed point numbers are encoded as strings, so this will result in additional memory allocation when used.
614    pub fn fix64(value: f64) -> Argument<String> {
615        Argument {
616            r#type: "Fix64",
617            value: value.to_string(),
618        }
619    }
620    /// Take a u64 and turn it into an argument. Integers are encoded as strings, so this will result in additional memory allocation when used.
621    pub fn uint64(value: u64) -> Argument<String> {
622        Argument {
623            r#type: "UInt64",
624            value: value.to_string(),
625        }
626    }
627    /// Take a i64 and turn it into an argument. Integers are encoded as strings, so this will result in additional memory allocation when used.
628    pub fn int64(value: i64) -> Argument<String> {
629        Argument {
630            r#type: "Int64",
631            value: value.to_string(),
632        }
633    }
634    /// Take a hex-encoded string and turn it into an argument.
635    pub fn address(value: String) -> Argument<String> {
636        Argument {
637            r#type: "Address",
638            value,
639        }
640    }
641    // process and encode bytes argument
642    pub fn encode(&self) -> Vec<u8> {
643        to_vec(&json!(self)).unwrap()
644    }
645}
646/// Utility function. Provides the ability to
647fn padding(vec: &mut Vec<u8>, count: usize) {
648    let mut i: usize = count;
649    i -= vec.len();
650    while i > 0 {
651        vec.push(0);
652        i -= 1;
653    }
654}
655/// Construct a signature object. Pass this into the payload
656/// or envelope signatures when signing a transaction.
657pub struct Sign {
658    pub address: String,
659    pub key_id: u32,
660    pub private_key: String,
661}
662/// build_transaction will construct a `flow::Transaction` with the provided script and arguments.
663/// See the `Argument` struct for details on how to construct arguments.
664pub async fn build_transaction(
665    script: Vec<u8>,
666    arguments: Vec<Vec<u8>>,
667    reference_block_id: Vec<u8>,
668    gas_limit: u64,
669    proposer: TransactionProposalKey,
670    authorizers: Vec<String>,
671    payer: String,
672) -> Result<Transaction, Box<dyn error::Error>> {
673    Ok(Transaction {
674        script,
675        arguments,
676        reference_block_id,
677        gas_limit,
678        proposal_key: Some(proposer),
679        authorizers: authorizers
680            .iter()
681            .map(|x| hex::decode(x).unwrap())
682            .collect(),
683        payload_signatures: vec![],
684        envelope_signatures: vec![],
685        payer: hex::decode(payer).unwrap(),
686    })
687}
688/// Provides an envelope of the given transaction
689fn envelope_from_transaction(
690    transaction: Transaction,
691    payload_signatures: &[TransactionSignature],
692) -> Vec<u8> {
693    let proposal_key = transaction.proposal_key.unwrap();
694    let mut proposal_address = proposal_key.address;
695    padding(&mut proposal_address, 8);
696    let mut ref_block = transaction.reference_block_id;
697    padding(&mut ref_block, 32);
698    let mut stream = RlpStream::new_list(2);
699
700    stream.begin_list(9);
701    stream.append(&Bytes::from(transaction.script).to_vec());
702    stream.begin_list(transaction.arguments.len());
703    for (_i, arg) in transaction.arguments.into_iter().enumerate() {
704        stream.append(&Bytes::from(arg).to_vec());
705    }
706
707    stream.append(&Bytes::from(ref_block).to_vec());
708    stream.append(&transaction.gas_limit);
709    stream.append(&Bytes::from(proposal_address).to_vec());
710    stream.append(&proposal_key.key_id);
711    stream.append(&proposal_key.sequence_number);
712    stream.append(&Bytes::from(transaction.payer).to_vec());
713
714    stream.begin_list(transaction.authorizers.len());
715    for (_i, auth) in transaction.authorizers.into_iter().enumerate() {
716        stream.append(&Bytes::from(auth).to_vec());
717    }
718
719    stream.begin_list(payload_signatures.len());
720    for (i, sig) in payload_signatures.iter().enumerate() {
721        let signature = sig.signature.to_vec();
722        stream.begin_list(3);
723        stream.append(&(i as u32));
724        stream.append(&sig.key_id);
725        stream.append(&signature);
726    }
727
728    stream.out().to_vec()
729}
730/// Provides a payload from a transaction
731fn payload_from_transaction(transaction: Transaction) -> Vec<u8> {
732    let proposal_key = transaction.proposal_key.unwrap();
733    let mut proposal_address = proposal_key.address;
734    padding(&mut proposal_address, 8);
735    let mut ref_block = transaction.reference_block_id;
736    padding(&mut ref_block, 32);
737
738    let mut stream = RlpStream::new_list(9);
739    stream.append(&Bytes::from(transaction.script).to_vec());
740    stream.begin_list(transaction.arguments.len());
741    for (_i, arg) in transaction.arguments.into_iter().enumerate() {
742        stream.append(&Bytes::from(arg).to_vec());
743    }
744
745    stream.append(&Bytes::from(ref_block).to_vec());
746    stream.append(&transaction.gas_limit);
747    stream.append(&Bytes::from(proposal_address).to_vec());
748    stream.append(&proposal_key.key_id);
749    stream.append(&proposal_key.sequence_number);
750    stream.append(&Bytes::from(transaction.payer).to_vec());
751
752    stream.begin_list(transaction.authorizers.len());
753    for (_i, auth) in transaction.authorizers.into_iter().enumerate() {
754        stream.append(&Bytes::from(auth).to_vec());
755    }
756    stream.out().to_vec()
757}
758/// Returns the provided message as bytes, signed by the private key.
759fn sign(message: Vec<u8>, private_key: String) -> Result<Vec<u8>, Box<dyn error::Error>> {
760    let secret_key = SecretKey::from_be_bytes(&hex::decode(private_key)?)?;
761    let sig_key = SigningKey::from(secret_key);
762    let signature = sig_key.sign(&message);
763    Ok(signature.as_bytes().to_vec())
764}
765/// Process key arguments. Intended for use with `create_account`
766pub fn process_keys_args(account_keys: Vec<String>) -> Argument<Vec<Value>> {
767    // do special processing for the keys, wrapping with algo, hash, and weight information:
768    // algo: ECDSA_P256
769    // hash: SHA3_256
770    // weight: 1000
771    Argument::array(
772        account_keys
773            .into_iter()
774            .map(|x| json!(Argument::string(format!("f847b840{}02038203e8", x))))
775            .collect::<Vec<Value>>(),
776    )
777}
778/// Sign the provided transaction.
779/// You will first need to `build_transaction`.
780pub async fn sign_transaction(
781    built_transaction: Transaction,
782    payload_signatures: Vec<&Sign>,
783    envelope_signatures: Vec<&Sign>,
784) -> Result<Option<Transaction>, Box<dyn error::Error>> {
785    let mut payload: Vec<TransactionSignature> = vec![];
786    let mut envelope: Vec<TransactionSignature> = vec![];
787    // for each of the payload private keys, sign the transaction
788    for signer in payload_signatures {
789        let encoded_payload: &[u8] = &payload_from_transaction(built_transaction.clone());
790        let mut domain_tag: Vec<u8> = b"FLOW-V0.0-transaction".to_vec();
791        // we need to pad 0s at the end of the domain_tag
792        padding(&mut domain_tag, 32);
793
794        let fully_encoded: Vec<u8> = [&domain_tag, encoded_payload].concat();
795        let mut addr = hex::decode(signer.address.clone()).unwrap();
796        padding(&mut addr, 8);
797
798        payload.push(TransactionSignature {
799            address: addr,
800            key_id: signer.key_id,
801            signature: sign(fully_encoded, signer.private_key.clone())?,
802        });
803    }
804    // for each of the envelope private keys, sign the transaction
805    for signer in envelope_signatures {
806        let encoded_payload: &[u8] =
807            &envelope_from_transaction(built_transaction.clone(), &payload);
808        let mut domain_tag: Vec<u8> = b"FLOW-V0.0-transaction".to_vec();
809        // we need to pad 0s at the end of the domain_tag
810        padding(&mut domain_tag, 32);
811
812        let fully_encoded: Vec<u8> = [&domain_tag, encoded_payload].concat();
813        let mut addr = hex::decode(signer.address.clone()).unwrap();
814        padding(&mut addr, 8);
815
816        envelope.push(TransactionSignature {
817            address: addr,
818            key_id: signer.key_id,
819            signature: sign(fully_encoded, signer.private_key.clone())?,
820        });
821    }
822    let signed_transaction = Some(Transaction {
823        script: built_transaction.script,
824        arguments: built_transaction.arguments,
825        reference_block_id: built_transaction.reference_block_id,
826        gas_limit: built_transaction.gas_limit,
827        proposal_key: built_transaction.proposal_key,
828        authorizers: built_transaction.authorizers,
829        payload_signatures: payload,
830        envelope_signatures: envelope,
831        payer: built_transaction.payer,
832    });
833    Ok(signed_transaction)
834}
835
836// ****************************************************
837// Testing
838// ****************************************************
839
840#[cfg(test)]
841mod tests {
842    #[tokio::test]
843    async fn meaningful_test() {
844        println!("does not exist yet. :)")
845    }
846}