-
Notifications
You must be signed in to change notification settings - Fork 24
/
Copy pathAddrStd.kt
175 lines (153 loc) · 5.79 KB
/
AddrStd.kt
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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
@file:Suppress("NOTHING_TO_INLINE")
package org.ton.block
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import org.ton.bigint.toBigInt
import org.ton.bitstring.BitString
import org.ton.bitstring.toBitString
import org.ton.cell.CellBuilder
import org.ton.cell.CellSlice
import org.ton.cell.invoke
import org.ton.crypto.base64
import org.ton.crypto.base64url
import org.ton.crypto.crc16
import org.ton.crypto.hex
import org.ton.tlb.TlbCodec
import org.ton.tlb.TlbConstructor
import org.ton.tlb.loadTlb
import org.ton.tlb.storeTlb
import kotlin.experimental.and
import kotlin.experimental.or
inline fun AddrStd(address: String): AddrStd = AddrStd.parse(address)
@Serializable
@SerialName("addr_std")
data class AddrStd(
val anycast: Maybe<Anycast>,
override val workchain_id: Int,
val address: BitString
) : MsgAddressInt {
init {
require(address.size == 256) { "address.size expected: 256 actual: ${address.size}" }
}
constructor(workchainId: Int, address: BitString) : this(null, workchainId, address)
constructor(workchainId: Int, address: ByteArray) : this(null, workchainId, address)
constructor(anycast: Anycast?, workchainId: Int, address: ByteArray) : this(
anycast.toMaybe(),
workchainId,
address.toBitString()
)
constructor(anycast: Anycast?, workchainId: Int, address: BitString) : this(
anycast.toMaybe(),
workchainId,
address
)
override fun toString(): String = "addr_std(anycast:$anycast workchain_id:$workchain_id address:$address)"
fun toString(
userFriendly: Boolean = true,
urlSafe: Boolean = true,
testOnly: Boolean = false,
bounceable: Boolean = true
): String = toString(this, userFriendly, urlSafe, testOnly, bounceable)
companion object : TlbCodec<AddrStd> by AddrStdTlbConstructor {
@JvmStatic
fun tlbCodec(): TlbConstructor<AddrStd> = AddrStdTlbConstructor
@JvmStatic
fun toString(
address: AddrStd,
userFriendly: Boolean = true,
urlSafe: Boolean = true,
testOnly: Boolean = false,
bounceable: Boolean = true
): String {
return if (userFriendly) {
val raw = byteArrayOf(tag(testOnly, bounceable), address.workchain_id.toByte()) +
address.address.toByteArray() + crc(address, testOnly, bounceable).toShort().toBigInt()
.toByteArray()
if (urlSafe) {
base64url(raw)
} else {
base64(raw)
}
} else {
address.workchain_id.toString() + ":" + hex(address.address.toByteArray())
}
}
@JvmStatic
fun parse(address: String): AddrStd {
return if (address.contains(':')) {
parseRaw(address)
} else {
parseUserFriendly(address)
}
}
@JvmStatic
fun parseRaw(address: String): AddrStd {
require(address.contains(':'))
// 32 bytes, each represented as 2 characters
require(address.substringAfter(':').length == 32 * 2)
return AddrStd(
// toByte() to make sure it fits into 8 bits
workchainId = address.substringBefore(':').toByte().toInt(),
address = hex(address.substringAfter(':'))
)
}
@JvmStatic
fun parseUserFriendly(address: String): AddrStd {
val raw = try {
base64url(address)
} catch (E: Exception) {
base64(address)
}
require(raw.size == 36) { "invalid byte-array size expected: 36, actual: ${raw.size}" }
check((raw[0] == 0x11.toByte()) or (raw[0] == 0x51.toByte())) {
"unknown address tag"
}
val addrStd = AddrStd(
workchainId = raw[1].toInt(),
address = raw.sliceArray(2..33)
)
val testOnly = raw[0] and 0x80.toByte() != 0.toByte()
if (testOnly) {
// not 0x80 = 0x7F; here we clean the test only flag
raw[0] = raw[0] and 0x7F.toByte()
}
val bounceable = raw[0] == 0x11.toByte()
val expectedChecksum = raw[34].toUByte().toInt() * 256 + raw[35].toUByte().toInt()
val actualChecksum = crc(addrStd, testOnly, bounceable)
check(expectedChecksum == actualChecksum) {
"CRC check failed"
}
return addrStd
}
private fun crc(address: AddrStd, testOnly: Boolean, bounceable: Boolean): Int =
crc16(
byteArrayOf(tag(testOnly, bounceable), address.workchain_id.toByte()),
address.address.toByteArray()
)
// Get the tag byte based on set flags
private fun tag(testOnly: Boolean, bounceable: Boolean): Byte =
(if (testOnly) 0x80.toByte() else 0.toByte()) or
(if (bounceable) 0x11.toByte() else 0x51.toByte())
}
}
private object AddrStdTlbConstructor : TlbConstructor<AddrStd>(
schema = "addr_std\$10 anycast:(Maybe Anycast) workchain_id:int8 address:bits256 = MsgAddressInt;"
) {
private val MaybeAnycast = Maybe(Anycast)
override fun storeTlb(
cellBuilder: CellBuilder,
value: AddrStd
) = cellBuilder {
storeTlb(MaybeAnycast, value.anycast)
storeInt(value.workchain_id, 8)
storeBits(value.address)
}
override fun loadTlb(
cellSlice: CellSlice
): AddrStd = cellSlice {
val anycast = loadTlb(MaybeAnycast)
val workchainId = loadInt(8).toInt()
val address = loadBits(256)
AddrStd(anycast, workchainId, address)
}
}