1
// Copyright © 2017-2020 Trust Wallet.
2
//
3
// This file is part of Trust. The full Trust copyright notice, including
4
// terms governing use, modification, and redistribution, is contained in the
5
// file LICENSE at the root of the source code distribution tree.
6

7
#pragma once
8

9
#include "Address.h"
10
#include "../Base58.h"
11
#include "../BinaryCoding.h"
12

13
#include <vector>
14

15
namespace TW::Solana {
16

17
template <typename T>
18 1
Data shortVecLength(std::vector<T> vec) {
19 1
    auto bytes = Data();
20 1
    auto remLen = vec.size();
21 1
    while (true) {
22 1
        uint8_t elem = remLen & 0x7f;
23 1
        remLen >>= 7;
24 1
        if (remLen == 0) {
25 1
            bytes.push_back(elem);
26 1
            break;
27
        } else {
28 0
            elem |= 0x80;
29 0
            bytes.push_back(elem);
30
        }
31
    }
32 1
    return bytes;
33
}
34

35
// System instruction types
36
enum SystemInstruction { CreateAccount, Assign, Transfer, CreateAccountWithSeed };
37

38
// Stake instruction types
39
enum StakeInstruction {
40
    Initialize = 0,
41
    DelegateStake = 2,
42
    Withdraw = 4,
43
    Deactivate = 5,
44
};
45

46
// An instruction to execute a program
47 1
struct CompiledInstruction {
48
    // Index into the transaction keys array indicating the program account that
49
    // executes this instruction
50
    uint8_t programIdIndex;
51
    // Ordered indices into the transaction keys array indicating which accounts
52
    // to pass to the program
53
    Data accounts;
54
    // The program input data
55
    Data data;
56

57 1
    CompiledInstruction(uint8_t programIdIndex, const Data& accounts, const Data& data)
58 1
        : programIdIndex(programIdIndex), accounts(accounts), data(data) {}
59

60
    // This constructor creates a default System Transfer instruction
61 1
    CompiledInstruction(uint8_t programIdIndex, Data accountIndexes, uint64_t value)
62 1
        : programIdIndex(programIdIndex) {
63 1
        this->accounts = accountIndexes;
64 1
        SystemInstruction type = Transfer;
65 1
        auto data = Data();
66 1
        encode32LE(static_cast<uint32_t>(type), data);
67 1
        encode64LE(static_cast<uint64_t>(value), data);
68 1
        this->data = data;
69
    }
70

71
    // This constructor creates a System CreateAccountWithSeed instruction
72 1
    CompiledInstruction(uint8_t programIdIndex, uint64_t value, uint64_t space, Address programId,
73
                        Address voteAddress, uint64_t seedLength, Address signer)
74 1
        : programIdIndex(programIdIndex) {
75 1
        std::vector<uint8_t> accounts = {0, 1};
76 1
        this->accounts = accounts;
77 1
        SystemInstruction type = CreateAccountWithSeed;
78 1
        auto data = Data();
79 1
        std::string seed = voteAddress.string();
80 1
        Data vecSeed(seed.begin(), seed.end());
81 1
        vecSeed.resize(static_cast<size_t>(seedLength));
82 1
        encode32LE(static_cast<uint32_t>(type), data);
83 1
        append(data, signer.vector());
84 1
        encode64LE(static_cast<uint64_t>(seedLength), data);
85 1
        append(data, vecSeed);
86 1
        encode64LE(static_cast<uint64_t>(value), data);
87 1
        encode64LE(static_cast<uint64_t>(space), data);
88 1
        append(data, programId.vector());
89 1
        this->data = data;
90
    }
91

92
    // This constructor creates an Initialize Stake instruction
93 1
    CompiledInstruction(uint8_t programIdIndex, StakeInstruction type, Address signer)
94 1
        : programIdIndex(programIdIndex) {
95 1
        std::vector<uint8_t> accounts = {1, 2};
96 1
        this->accounts = accounts;
97 1
        auto data = Data();
98 1
        encode32LE(static_cast<uint32_t>(type), data);
99 1
        append(data, signer.vector());
100 1
        append(data, signer.vector());
101 1
        auto lockup = Data(48);
102 1
        append(data, lockup);
103 1
        this->data = data;
104
    }
105

106
    // This constructor creates a Withdraw Stake instruction
107 1
    CompiledInstruction(uint8_t programIdIndex, StakeInstruction type, uint64_t value)
108 1
        : programIdIndex(programIdIndex) {
109 1
        std::vector<uint8_t> accounts = {1, 0, 2, 3};
110 1
        this->accounts = accounts;
111 1
        auto data = Data();
112 1
        encode32LE(static_cast<uint32_t>(type), data);
113 1
        encode64LE(static_cast<uint64_t>(value), data);
114 1
        this->data = data;
115
    }
116

117
    // This constructor creates a Stake instruction
118 1
    CompiledInstruction(uint8_t programIdIndex, StakeInstruction type)
119 1
        : programIdIndex(programIdIndex) {
120 1
        std::vector<uint8_t> accounts;
121 1
        auto data = Data();
122 1
        encode32LE(static_cast<uint32_t>(type), data);
123 1
        if (type == DelegateStake) {
124 1
            accounts = {1, 3, 4, 5, 0};
125 1
        } else if (type == Deactivate) {
126 1
            accounts = {1, 2, 0};
127
        }
128 1
        this->accounts = accounts;
129 1
        this->data = data;
130
    }
131
};
132

133
class Hash {
134
  public:
135
    static const size_t size = 32;
136
    /// Hash data
137
    std::array<uint8_t, size> bytes;
138

139 1
    Hash(const std::string& string) {
140 1
        const auto data = Base58::bitcoin.decode(string);
141 1
        std::copy(data.begin(), data.end(), this->bytes.begin());
142
    }
143
};
144

145
class Signature {
146
  public:
147
    static const size_t size = 64;
148
    /// Signature data
149
    std::array<uint8_t, size> bytes;
150

151 1
    Signature(const std::string& string) {
152 1
        const auto data = Base58::bitcoin.decode(string);
153 1
        std::copy(data.begin(), data.end(), this->bytes.begin());
154
    }
155 1
    Signature(const std::array<uint8_t, 64>& bytes) { this->bytes = bytes; }
156 1
    Signature(const Data& bytes) { std::copy(bytes.begin(), bytes.end(), this->bytes.begin()); }
157

158
    bool operator==(const Signature& v) const;
159
};
160

161
struct MessageHeader {
162
    // The number of signatures required for this message to be considered
163
    // valid. The signatures must match the first `numRequiredSignatures` of
164
    // `accountKeys`.
165
    uint8_t numRequiredSignatures;
166
    // The last numCreditOnlySignedAccounts of the signed keys are
167
    // credit-only accounts.
168
    uint8_t numCreditOnlySignedAccounts;
169
    // The last numCreditOnlyUnsignedAccounts of the unsigned keys are
170
    // credit-only accounts.
171
    uint8_t numCreditOnlyUnsignedAccounts;
172
};
173

174 1
class Message {
175
  public:
176
    // The message header, identifying signed and credit-only `accountKeys`
177
    MessageHeader header;
178
    // All the account keys used by this transaction
179
    std::vector<Address> accountKeys;
180
    // The id of a recent ledger entry.
181
    Hash recentBlockhash;
182
    // Programs that will be executed in sequence and committed in one atomic
183
    // transaction if all succeed.
184
    std::vector<CompiledInstruction> instructions;
185

186 1
    Message() : recentBlockhash(Hash("11111111111111111111111111111111")){};
187

188 1
    Message(MessageHeader header, std::vector<Address> accountKeys, Hash recentBlockhash,
189
            std::vector<CompiledInstruction> instructions)
190 1
        : header(header)
191 1
        , accountKeys(accountKeys)
192 1
        , recentBlockhash(recentBlockhash)
193 1
        , instructions(instructions) {}
194

195
    // This constructor creates a default single-signer Transfer message
196 1
    Message(Address from, Address to, uint64_t value, Hash recentBlockhash)
197 1
        : recentBlockhash(recentBlockhash) {
198 1
        MessageHeader header = {1, 0, 1};
199 1
        this->header = header;
200 1
        auto programId = Address("11111111111111111111111111111111");
201 1
        std::vector<Address> accountKeys;
202 1
        Data accountIndexes;
203
        uint8_t programIdIndex;
204 1
        if (from.vector() != to.vector()) {
205 1
            accountKeys = {from, to, programId};
206 1
            accountIndexes = {0, 1};
207 1
            programIdIndex = 2;
208 1
        } else {
209 1
            accountKeys = {from, programId};
210 1
            accountIndexes = {0, 0};
211 1
            programIdIndex = 1;
212
        }
213 1
        this->accountKeys = accountKeys;
214 1
        std::vector<CompiledInstruction> instructions;
215 1
        auto instruction = CompiledInstruction(programIdIndex, accountIndexes, value);
216 1
        instructions.push_back(instruction);
217 1
        this->instructions = instructions;
218
    }
219

220
    // This constructor creates a create_account_with_seed_and_delegate_stake message
221 1
    Message(Address signer, Address stakeAddress, Address voteAddress, uint64_t value,
222
            Hash recentBlockhash)
223 1
        : recentBlockhash(recentBlockhash) {
224 1
        MessageHeader header = {1, 0, 6};
225 1
        this->header = header;
226

227 1
        auto sysvarRentId = Address("SysvarRent111111111111111111111111111111111");
228 1
        auto sysvarClockId = Address("SysvarC1ock11111111111111111111111111111111");
229 1
        auto stakeConfigId = Address("StakeConfig11111111111111111111111111111111");
230 1
        auto systemProgramId = Address("11111111111111111111111111111111");
231 1
        auto stakeProgramId = Address(STAKE_ADDRESS);
232 1
        std::vector<Address> accountKeys = {signer,          stakeAddress,  sysvarRentId,
233 1
                                            voteAddress,     sysvarClockId, stakeConfigId,
234 1
                                            systemProgramId, stakeProgramId};
235 1
        this->accountKeys = accountKeys;
236

237 1
        std::vector<CompiledInstruction> instructions;
238
        // create_account_with_seed instruction
239
        auto createAccountInstruction =
240 1
            CompiledInstruction(6, value, 2008, stakeProgramId, voteAddress, 32, signer);
241 1
        instructions.push_back(createAccountInstruction);
242
        // initialize instruction
243 1
        auto initializeInstruction = CompiledInstruction(7, Initialize, signer);
244 1
        instructions.push_back(initializeInstruction);
245
        // delegate_stake instruction
246 1
        auto delegateInstruction = CompiledInstruction(7, DelegateStake);
247 1
        instructions.push_back(delegateInstruction);
248

249 1
        this->instructions = instructions;
250
    }
251

252
    // This constructor creates a deactivate_stake message
253 1
    Message(Address signer, Address stakeAddress, StakeInstruction type, Hash recentBlockhash)
254 1
        : recentBlockhash(recentBlockhash) {
255 1
        MessageHeader header = {1, 0, 2};
256 1
        this->header = header;
257

258 1
        auto sysvarClockId = Address("SysvarC1ock11111111111111111111111111111111");
259 1
        auto programId = Address("Stake11111111111111111111111111111111111111");
260 1
        std::vector<Address> accountKeys = {signer, stakeAddress, sysvarClockId, programId};
261 1
        this->accountKeys = accountKeys;
262 1
        std::vector<CompiledInstruction> instructions;
263 1
        auto instruction = CompiledInstruction(3, Deactivate);
264 1
        instructions.push_back(instruction);
265 1
        this->instructions = instructions;
266
    }
267

268
    // This constructor creates a withdraw message, with the signer as the default recipient
269 1
    Message(Address signer, Address stakeAddress, uint64_t value, StakeInstruction type,
270
            Hash recentBlockhash)
271 1
        : recentBlockhash(recentBlockhash) {
272 1
        MessageHeader header = {1, 0, 3};
273 1
        this->header = header;
274

275 1
        auto sysvarClockId = Address("SysvarC1ock11111111111111111111111111111111");
276 1
        auto sysvarStakeHistoryId = Address("SysvarStakeHistory1111111111111111111111111");
277 1
        auto programId = Address("Stake11111111111111111111111111111111111111");
278 1
        std::vector<Address> accountKeys = {signer, stakeAddress, sysvarClockId,
279 1
                                            sysvarStakeHistoryId, programId};
280 1
        this->accountKeys = accountKeys;
281 1
        std::vector<CompiledInstruction> instructions;
282 1
        auto instruction = CompiledInstruction(4, Withdraw, value);
283 1
        instructions.push_back(instruction);
284 1
        this->instructions = instructions;
285
    }
286
};
287

288 1
class Transaction {
289
  public:
290
    // Signatures
291
    std::vector<Signature> signatures;
292
    // The message to sign
293
    Message message;
294

295 1
    Transaction(Message message) : message(message) {
296 1
        this->signatures.resize(message.header.numRequiredSignatures, Signature(defaultSignature));
297
    }
298

299
    // Default basic transfer transaction
300 1
    Transaction(Address from, Address to, uint64_t value, Hash recentBlockhash)
301 1
        : message(Message(from, to, value, recentBlockhash)) {
302 1
        this->signatures.resize(1, Signature(defaultSignature));
303
    }
304

305
  public:
306
    std::string serialize() const;
307
    std::vector<uint8_t> messageData() const;
308
    uint8_t getAccountIndex(Address publicKey);
309

310
  private:
311
    std::array<uint8_t, 64> defaultSignature;
312
};
313

314
} // namespace TW::Solana
315

316
/// Wrapper for C interface.
317
struct TWSolanaTransaction {
318
    TW::Solana::Transaction impl;
319
};

Read our documentation on viewing source code .

Loading