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 "../BinaryCoding.h"
10
#include "../Data.h"
11
#include "../PublicKey.h"
12
#include "../SS58Address.h"
13
#include <boost/multiprecision/cpp_int.hpp>
14
#include <cmath>
15
#include <algorithm>
16
#include <bitset>
17

18

19
/// Reference https://github.com/soramitsu/kagome/blob/master/core/scale/scale_encoder_stream.cpp
20
using CompactInteger = boost::multiprecision::cpp_int;
21

22
namespace TW::Polkadot {
23

24
static constexpr size_t kMinUint16 = (1ul << 6u);
25
static constexpr size_t kMinUint32 = (1ul << 14u);
26
static constexpr size_t kMinBigInteger = (1ul << 30u);
27

28 1
inline size_t countBytes(CompactInteger value) {
29 1
    if (0 == value) {
30 0
        return 1;
31
    }
32

33 1
    size_t size = 0;
34 1
    while (value > 0) {
35 1
        ++size;
36 1
        value >>= 8;
37
    }
38

39 1
    return size;
40
}
41

42 1
inline Data encodeCompact(CompactInteger value) {
43 1
    auto data = Data{};
44

45 1
    if (value < kMinUint16) {
46 1
        auto v = value.convert_to<uint8_t>() << 2u;
47 1
        data.push_back(static_cast<uint8_t>(v));
48 1
        return data;
49 1
    } else if (value < kMinUint32) {
50 1
        auto v = (value.convert_to<uint16_t>() << 2u);
51 1
        v += 0x01; // set 0b01 flag
52 1
        auto minor_byte = static_cast<uint8_t>(v & 0xffu);
53 1
        data.push_back(minor_byte);
54 1
        v >>= 8u;
55 1
        auto major_byte = static_cast<uint8_t>(v & 0xffu);
56 1
        data.push_back(major_byte);
57 1
        return data;
58 1
    } else if (value < kMinBigInteger) {
59 1
        uint32_t v = (value.convert_to<uint32_t>() << 2u);
60 1
        v += 0x02; // set 0b10 flag
61 1
        encode32LE(v, data);
62 1
        return data;
63
    }
64

65 1
    auto length = countBytes(value);
66 1
    if (length > 67) {
67
        // too big
68 0
        return data;
69
    }
70 1
    uint8_t header = (static_cast<uint8_t>(length) - 4) * 4;
71 1
    header += 0x03; // set 0b11 flag;
72 1
    data.push_back(header);
73

74 1
    auto v = CompactInteger{value};
75 1
    for (size_t i = 0; i < length; ++i) {
76 1
        data.push_back(static_cast<uint8_t>(v & 0xff)); // push back least significant byte
77 1
        v >>= 8;
78
    }
79 1
    return data;
80
}
81

82
// append length prefix
83 1
inline void encodeLengthPrefix(Data& data) {
84 1
    size_t len = data.size();
85 1
    auto prefix = encodeCompact(len);
86 1
    data.insert(data.begin(), prefix.begin(), prefix.end());
87
}
88

89 1
inline Data encodeBool(bool value) {
90 1
    return Data{uint8_t(value ? 0x01 : 0x00)};
91
}
92

93 1
inline Data encodeVector(std::vector<Data>& vec) {
94 1
    auto data = encodeCompact(vec.size());
95 1
    for (auto v : vec) {
96 1
        append(data, v);
97
    }
98 1
    return data;
99
}
100

101 1
inline Data encodeAddress(const PublicKey& key) {
102 1
    auto data = Data{};
103 1
    append(data, Data(key.bytes.begin(), key.bytes.end()));
104 1
    return data;
105
}
106

107 1
inline Data encodeAddress(const SS58Address& address) {
108 1
    auto data = Data{};
109
    // first byte is network
110 1
    append(data, Data(address.bytes.begin() + 1, address.bytes.end()));
111 1
    return data;
112
}
113

114 1
inline Data encodeAddresses(const std::vector<SS58Address>& addresses) {
115 1
    std::vector<Data> vec;
116 1
    for (auto addr : addresses) {
117 1
        vec.push_back(encodeAddress(addr));
118
    }
119 1
    return encodeVector(vec);
120
}
121

122 1
inline Data encodeEra(const uint64_t block, const uint64_t period) {
123
    // MortalEra(phase, period)
124
    // See decodeMortalObject at https://github.com/polkadot-js/api/blob/master/packages/types/src/extrinsic/ExtrinsicEra.ts#L87
125
    // See toU8a at https://github.com/polkadot-js/api/blob/master/packages/types/src/extrinsic/ExtrinsicEra.ts#L167
126 1
    uint64_t calPeriod = uint64_t(pow(2, ceil(log2(double(period)))));
127 1
    calPeriod = std::min(std::max(calPeriod, uint64_t(4)), uint64_t(1) << 16);
128 1
    uint64_t phase = block % calPeriod;
129 1
    uint64_t quantizeFactor = std::max(calPeriod >> uint64_t(12), uint64_t(1));
130 1
    uint64_t quantizedPhase = phase / quantizeFactor * quantizeFactor;
131

132 1
    auto bitset = std::bitset<64>(calPeriod);
133 1
    int trailingZeros = 0;
134 1
    for (int i = 0; i < 64 - 1; i++) {
135 1
        if (bitset[i] == 0) {
136 1
            trailingZeros += 1;
137 1
        } else {
138 1
            break;
139
        }
140
    }
141 1
    auto encoded = std::min(15, std::max(1, trailingZeros - 1)) + (((quantizedPhase / quantizeFactor) << 4));
142 1
    return Data{byte(encoded & 0xff), byte(encoded >> 8)};
143
}
144

145
} // namespace TW::Polkadot

Read our documentation on viewing source code .

Loading