1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
// Copyright (c) The Diem Core Contributors
// SPDX-License-Identifier: Apache-2.0

use diem_sdk::{
    client::{views::TransactionDataView, SignedTransaction},
    crypto::{ed25519::Ed25519PrivateKey, PrivateKey, SigningKey, Uniform},
    transaction_builder::Currency,
    types::{account_config::XUS_NAME, transaction::authenticator::AuthenticationKey},
};
use forge::{PublicUsageContext, PublicUsageTest, Result, Test};

pub struct ExternalTransactionSigner;

impl Test for ExternalTransactionSigner {
    fn name(&self) -> &'static str {
        "smoke-test::external-transaction-signer"
    }
}

impl PublicUsageTest for ExternalTransactionSigner {
    fn run<'t>(&self, ctx: &mut PublicUsageContext<'t>) -> Result<()> {
        let client = ctx.client();

        // generate key pair
        let private_key = Ed25519PrivateKey::generate(ctx.rng());
        let public_key = private_key.public_key();

        // create transfer parameters
        let sender_auth_key = AuthenticationKey::ed25519(&public_key);
        let sender_address = sender_auth_key.derived_address();
        ctx.create_parent_vasp_account(sender_auth_key)?;
        ctx.fund(sender_address, 10_000_000)?;

        let receiver = ctx.random_account();
        ctx.create_parent_vasp_account(receiver.authentication_key())?;
        ctx.fund(receiver.address(), 1_000_000)?;

        let amount = 1_000_000;
        let test_gas_unit_price = 1;
        let test_max_gas_amount = 1_000_000;

        // prepare transfer transaction
        let test_sequence_number = client
            .get_account(sender_address)?
            .into_inner()
            .unwrap()
            .sequence_number;

        let currency_code = XUS_NAME;

        let unsigned_txn = ctx
            .transaction_factory()
            .with_diem_version(0) // Force Script not ScriptFunctions
            .peer_to_peer(Currency::XUS, receiver.address(), amount)
            .sender(sender_address)
            .sequence_number(test_sequence_number)
            .max_gas_amount(test_max_gas_amount)
            .gas_unit_price(test_gas_unit_price)
            .build();

        assert_eq!(unsigned_txn.sender(), sender_address);

        // sign the transaction with the private key
        let signature = private_key.sign(&unsigned_txn);

        // submit the transaction
        let txn = SignedTransaction::new(unsigned_txn, public_key, signature);
        client.submit(&txn)?;
        client.wait_for_signed_transaction(&txn, None, None)?;

        // query the transaction and check it contains the same values as requested
        let txn = client
            .get_account_transaction(sender_address, test_sequence_number, false)?
            .into_inner()
            .unwrap();

        match txn.transaction {
            TransactionDataView::UserTransaction {
                sender,
                sequence_number,
                max_gas_amount,
                gas_unit_price,
                gas_currency,
                script,
                ..
            } => {
                assert_eq!(sender, sender_address);
                assert_eq!(sequence_number, test_sequence_number);
                assert_eq!(gas_unit_price, test_gas_unit_price);
                assert_eq!(gas_currency, currency_code.to_string());
                assert_eq!(max_gas_amount, test_max_gas_amount);

                assert_eq!(script.r#type, "peer_to_peer_with_metadata");
                assert_eq!(script.type_arguments.unwrap(), vec!["XUS"]);
                assert_eq!(
                    script.arguments.unwrap(),
                    vec![
                        format!("{{ADDRESS: {:?}}}", receiver.address()),
                        format!("{{U64: {}}}", amount),
                        "{U8Vector: 0x}".to_string(),
                        "{U8Vector: 0x}".to_string()
                    ]
                );
                // legacy fields
                assert_eq!(script.receiver.unwrap(), receiver.address());
                assert_eq!(script.amount.unwrap(), amount);
                assert_eq!(script.currency.unwrap(), currency_code.to_string());
                assert!(script.metadata.unwrap().inner().is_empty());
                assert!(script.metadata_signature.unwrap().inner().is_empty());
            }
            _ => panic!("Query should get user transaction"),
        }
        Ok(())
    }
}