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
//! RLP encoding of transactions.

use rlp::RlpStream;

/// Encodes a transaction's envelope.
#[allow(clippy::too_many_arguments)]
pub fn rlp_encode_transaction_envelope(
    s: &mut RlpStream,
    script: impl AsRef<[u8]>,
    arguments: impl IntoIterator<IntoIter = impl ExactSizeIterator<Item = impl AsRef<[u8]>>>,
    reference_block_id: impl AsRef<[u8]>,
    gas_limit: u64,
    proposal_key_address: impl AsRef<[u8]>,
    proposal_key_id: u64,
    proposal_key_sequence_number: u64,
    payer: impl AsRef<[u8]>,
    authorizers: impl IntoIterator<IntoIter = impl ExactSizeIterator<Item = impl AsRef<[u8]>>>,
    payload_signatures: impl IntoIterator<
        IntoIter = impl ExactSizeIterator<Item = (u32, u32, impl AsRef<[u8]>)>,
    >,
) {
    s.begin_list(2);
    rlp_encode_transaction_payload(
        s,
        script,
        arguments,
        reference_block_id,
        gas_limit,
        proposal_key_address,
        proposal_key_id,
        proposal_key_sequence_number,
        payer,
        authorizers,
    );
    let payload_signatures = payload_signatures.into_iter();
    s.begin_list(payload_signatures.len());
    for (signer_index, key_id, signature) in payload_signatures {
        s.begin_list(3)
            .append(&signer_index)
            .append(&key_id)
            .append(&signature.as_ref());
    }
}

/// Encodes a transaction's payload.
#[allow(clippy::too_many_arguments)]
pub fn rlp_encode_transaction_payload(
    stream: &mut RlpStream,
    script: impl AsRef<[u8]>,
    arguments: impl IntoIterator<IntoIter = impl ExactSizeIterator<Item = impl AsRef<[u8]>>>,
    reference_block_id: impl AsRef<[u8]>,
    gas_limit: u64,
    proposal_key_address: impl AsRef<[u8]>,
    proposal_key_id: u64,
    proposal_key_sequence_number: u64,
    payer: impl AsRef<[u8]>,
    authorizers: impl IntoIterator<IntoIter = impl ExactSizeIterator<Item = impl AsRef<[u8]>>>,
) {
    stream.begin_list(9).append(&script.as_ref());

    let arguments = arguments.into_iter();

    stream.begin_list(arguments.len());

    for arg in arguments {
        stream.append(&arg.as_ref());
    }

    stream
        .append(&reference_block_id.as_ref())
        .append(&gas_limit)
        .append_iter(lpad(&proposal_key_address, 8))
        .append(&proposal_key_id)
        .append(&proposal_key_sequence_number)
        .append_iter(lpad(&payer, 8));

    let authorizers = authorizers.into_iter();

    stream.begin_list(authorizers.len());

    for authorizer in authorizers {
        stream.append_iter(lpad(&authorizer, 8));
    }
}

// Given a slice of bytes and the padded length, returns an iterator of the data left-padded with zeros.
fn lpad(data: &impl AsRef<[u8]>, padded_len: usize) -> impl Iterator<Item = u8> + '_ {
    let data = data.as_ref();

    std::iter::repeat(0)
        .take(padded_len - data.as_ref().len())
        .chain(data.iter().copied())
}