@alephium/web3 0.30.1 → 0.31.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -16,24 +16,179 @@ You should have received a copy of the GNU Lesser General Public License
16
16
  along with the library. If not, see <http://www.gnu.org/licenses/>.
17
17
  */
18
18
 
19
+ import { ec as EC } from 'elliptic'
20
+ import BN from 'bn.js'
21
+ import { TOTAL_NUMBER_OF_GROUPS } from '../constants'
22
+ import blake from 'blakejs'
19
23
  import bs58 from './bs58'
20
24
  import djb2 from './djb2'
25
+ import { binToHex, hexToBinUnsafe } from './utils'
26
+ import { KeyType } from '../signer'
21
27
 
22
- export function addressToGroup(address: string, totalNumberOfGroups: number): number {
23
- const bytes = bs58.decode(address).slice(1)
24
- const value = djb2(bytes) | 1
25
- const hash = toPosInt(xorByte(value))
26
- const group = hash % totalNumberOfGroups
28
+ const ec = new EC('secp256k1')
29
+
30
+ export enum AddressType {
31
+ P2PKH = 0x00,
32
+ P2MPKH = 0x01,
33
+ P2SH = 0x02,
34
+ P2C = 0x03
35
+ }
36
+
37
+ export function validateAddress(address: string) {
38
+ let decoded: Uint8Array
39
+ try {
40
+ decoded = bs58.decode(address)
41
+ } catch (_) {
42
+ throw new Error('Invalid base58 string')
43
+ }
44
+
45
+ if (decoded.length === 0) throw new Error('Address is empty')
46
+ const addressType = decoded[0]
47
+ if (addressType === AddressType.P2MPKH) {
48
+ // [1, n, ...hashes, m]
49
+ if ((decoded.length - 3) % 32 === 0) return
50
+ } else if (addressType === AddressType.P2PKH || addressType === AddressType.P2SH || addressType === AddressType.P2C) {
51
+ // [type, ...hash]
52
+ if (decoded.length === 33) return
53
+ }
54
+
55
+ throw new Error(`Invalid address: ${address}`)
56
+ }
57
+
58
+ export function groupOfAddress(address: string): number {
59
+ validateAddress(address)
60
+
61
+ const decoded = bs58.decode(address)
62
+ const addressType = decoded[0]
63
+ const addressBody = decoded.slice(1)
64
+
65
+ if (addressType == AddressType.P2PKH) {
66
+ return groupOfP2pkhAddress(addressBody)
67
+ } else if (addressType == AddressType.P2MPKH) {
68
+ return groupOfP2mpkhAddress(addressBody)
69
+ } else if (addressType == AddressType.P2SH) {
70
+ return groupOfP2shAddress(addressBody)
71
+ } else {
72
+ // Contract Address
73
+ const id = contractIdFromAddress(address)
74
+ return id[`${id.length - 1}`]
75
+ }
76
+ }
77
+
78
+ function groupOfAddressBytes(bytes: Uint8Array): number {
79
+ const hint = djb2(bytes) | 1
80
+ const hash = xorByte(hint)
81
+ const group = hash % TOTAL_NUMBER_OF_GROUPS
27
82
  return group
28
83
  }
29
84
 
30
- function xorByte(value: number): number {
31
- const byte0 = value >> 24
32
- const byte1 = value >> 16
33
- const byte2 = value >> 8
34
- return byte0 ^ byte1 ^ byte2 ^ value
85
+ // Pay to public key hash address
86
+ function groupOfP2pkhAddress(address: Uint8Array): number {
87
+ return groupOfAddressBytes(address)
88
+ }
89
+
90
+ // Pay to multiple public key hash address
91
+ function groupOfP2mpkhAddress(address: Uint8Array): number {
92
+ return groupOfAddressBytes(address.slice(1, 33))
93
+ }
94
+
95
+ // Pay to script hash address
96
+ function groupOfP2shAddress(address: Uint8Array): number {
97
+ return groupOfAddressBytes(address)
98
+ }
99
+
100
+ export function contractIdFromAddress(address: string): Uint8Array {
101
+ return idFromAddress(address)
102
+ }
103
+
104
+ export function tokenIdFromAddress(address: string): Uint8Array {
105
+ return idFromAddress(address)
106
+ }
107
+
108
+ function idFromAddress(address: string): Uint8Array {
109
+ const decoded = bs58.decode(address)
110
+
111
+ if (decoded.length == 0) throw new Error('Address string is empty')
112
+ const addressType = decoded[0]
113
+ const addressBody = decoded.slice(1)
114
+
115
+ if (addressType == AddressType.P2C) {
116
+ return addressBody
117
+ } else {
118
+ throw new Error(`Invalid contract address type: ${addressType}`)
119
+ }
120
+ }
121
+
122
+ export function groupOfPrivateKey(privateKey: string, keyType?: KeyType): number {
123
+ return groupOfAddress(addressFromPublicKey(publicKeyFromPrivateKey(privateKey, keyType), keyType))
124
+ }
125
+
126
+ export function publicKeyFromPrivateKey(privateKey: string, _keyType?: KeyType): string {
127
+ const keyType = _keyType ?? 'default'
128
+
129
+ if (keyType === 'default') {
130
+ const key = ec.keyFromPrivate(privateKey)
131
+ return key.getPublic(true, 'hex')
132
+ } else {
133
+ return ec.g.mul(new BN(privateKey, 16)).encode('hex', true).slice(2)
134
+ }
135
+ }
136
+
137
+ export function addressFromPublicKey(publicKey: string, _keyType?: KeyType): string {
138
+ const keyType = _keyType ?? 'default'
139
+
140
+ if (keyType === 'default') {
141
+ const addressType = Buffer.from([AddressType.P2PKH])
142
+ const hash = Buffer.from(blake.blake2b(Buffer.from(publicKey, 'hex'), undefined, 32))
143
+ const bytes = Buffer.concat([addressType, hash])
144
+ return bs58.encode(bytes)
145
+ } else {
146
+ const lockupScript = Buffer.from(`0101000000000458144020${publicKey}8685`, 'hex')
147
+ return addressFromScript(lockupScript)
148
+ }
149
+ }
150
+
151
+ export function addressFromScript(script: Uint8Array): string {
152
+ const scriptHash = blake.blake2b(script, undefined, 32)
153
+ const addressType = Buffer.from([AddressType.P2SH])
154
+ return bs58.encode(Buffer.concat([addressType, scriptHash]))
155
+ }
156
+
157
+ export function addressFromContractId(contractId: string): string {
158
+ const addressType = Buffer.from([AddressType.P2C])
159
+ const hash = Buffer.from(hexToBinUnsafe(contractId))
160
+ const bytes = Buffer.concat([addressType, hash])
161
+ return bs58.encode(bytes)
162
+ }
163
+
164
+ export function addressFromTokenId(tokenId: string): string {
165
+ const contractId = tokenId // contract ID is the same as token ID
166
+ return addressFromContractId(contractId)
167
+ }
168
+
169
+ export function contractIdFromTx(txId: string, outputIndex: number): string {
170
+ const txIdBin = hexToBinUnsafe(txId)
171
+ const data = Buffer.concat([txIdBin, Buffer.from([outputIndex])])
172
+ const hash = blake.blake2b(data, undefined, 32)
173
+ return binToHex(hash)
174
+ }
175
+
176
+ export function subContractId(parentContractId: string, pathInHex: string, group: number): string {
177
+ if (group < 0 || group >= TOTAL_NUMBER_OF_GROUPS) {
178
+ throw new Error(`Invalid group ${group}`)
179
+ }
180
+ const data = Buffer.concat([hexToBinUnsafe(parentContractId), hexToBinUnsafe(pathInHex)])
181
+ const bytes = Buffer.concat([
182
+ blake.blake2b(blake.blake2b(data, undefined, 32), undefined, 32).slice(0, -1),
183
+ Buffer.from([group])
184
+ ])
185
+ return binToHex(bytes)
35
186
  }
36
187
 
37
- function toPosInt(byte: number): number {
38
- return byte & 0xff
188
+ export function xorByte(intValue: number): number {
189
+ const byte0 = (intValue >> 24) & 0xff
190
+ const byte1 = (intValue >> 16) & 0xff
191
+ const byte2 = (intValue >> 8) & 0xff
192
+ const byte3 = intValue & 0xff
193
+ return (byte0 ^ byte1 ^ byte2 ^ byte3) & 0xff
39
194
  }
@@ -0,0 +1,77 @@
1
+ /*
2
+ Copyright 2018 - 2022 The Alephium Authors
3
+ This file is part of the alephium project.
4
+
5
+ The library is free software: you can redistribute it and/or modify
6
+ it under the terms of the GNU Lesser General Public License as published by
7
+ the Free Software Foundation, either version 3 of the License, or
8
+ (at your option) any later version.
9
+
10
+ The library is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ GNU Lesser General Public License for more details.
14
+
15
+ You should have received a copy of the GNU Lesser General Public License
16
+ along with the library. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ class CompilationError {
20
+ constructor(
21
+ public lineStart: number,
22
+ public column: number,
23
+ public errorType: string,
24
+ public line: number,
25
+ public codeLine: string,
26
+ public errorIndicator: string,
27
+ public message: string,
28
+ public additionalLine1?: string,
29
+ public additionalLine2?: string
30
+ ) {}
31
+
32
+ reformat(line: number, file: string): string {
33
+ const spaces = `${line}`.replace(/\d/g, ' ')
34
+ const newError = `${file} (${line}:${this.column}): ${this.errorType}
35
+ ${line} |${this.codeLine}
36
+ ${spaces} |${this.errorIndicator}
37
+ ${spaces} |${this.message}`
38
+
39
+ if (this.additionalLine1 && this.additionalLine2) {
40
+ return `${newError}\n${spaces} |${this.additionalLine1}\n${spaces} |${this.additionalLine2}`
41
+ } else {
42
+ return newError
43
+ }
44
+ }
45
+ }
46
+
47
+ const errorRegex = /error \((\d+):(\d+)\):\s*(.*)\n\s*(\d+)\s*\|(.*)\n.*\|(.*)\n\s*\|(.*)(?:\n\s*\|(.*)\n\s*\|(.*))?/
48
+
49
+ export function parseError(error: string): CompilationError | undefined {
50
+ const match = error.match(errorRegex)
51
+
52
+ if (match) {
53
+ const lineStart = parseInt(match[1])
54
+ const column = parseInt(match[2])
55
+ const errorType = match[3]
56
+ const line = parseInt(match[4])
57
+ const codeLine = match[5]
58
+ const errorIndicator = match[6]
59
+ const message = match[7]
60
+ const additionalLine1 = match[8]
61
+ const additionalLine2 = match[9]
62
+
63
+ return new CompilationError(
64
+ lineStart,
65
+ column,
66
+ errorType,
67
+ line,
68
+ codeLine,
69
+ errorIndicator,
70
+ message,
71
+ additionalLine1,
72
+ additionalLine2
73
+ )
74
+ } else {
75
+ undefined
76
+ }
77
+ }
@@ -18,12 +18,10 @@ along with the library. If not, see <http://www.gnu.org/licenses/>.
18
18
 
19
19
  import { ec as EC, SignatureInput } from 'elliptic'
20
20
  import BN from 'bn.js'
21
- import blake from 'blakejs'
22
21
  import bs58 from './bs58'
23
22
  import { Buffer } from 'buffer/'
24
23
 
25
24
  import { TOTAL_NUMBER_OF_GROUPS, TOTAL_NUMBER_OF_CHAINS } from '../constants'
26
- import djb2 from './djb2'
27
25
  import { KeyType } from '../signer'
28
26
  import { HexString } from '../contract'
29
27
 
@@ -63,14 +61,6 @@ export function signatureDecode(ec: EC, signature: string): SignatureInput {
63
61
  }
64
62
  }
65
63
 
66
- export function xorByte(intValue: number): number {
67
- const byte0 = (intValue >> 24) & 0xff
68
- const byte1 = (intValue >> 16) & 0xff
69
- const byte2 = (intValue >> 8) & 0xff
70
- const byte3 = intValue & 0xff
71
- return (byte0 ^ byte1 ^ byte2 ^ byte3) & 0xff
72
- }
73
-
74
64
  export function isHexString(input: string): boolean {
75
65
  return input.length % 2 === 0 && /^[0-9a-fA-F]*$/.test(input)
76
66
  }
@@ -84,85 +74,6 @@ export function toNonNegativeBigInt(input: string): bigint | undefined {
84
74
  }
85
75
  }
86
76
 
87
- export enum AddressType {
88
- P2PKH = 0x00,
89
- P2MPKH = 0x01,
90
- P2SH = 0x02,
91
- P2C = 0x03
92
- }
93
-
94
- export function groupOfAddress(address: string): number {
95
- const decoded = bs58.decode(address)
96
-
97
- if (decoded.length == 0) throw new Error('Address string is empty')
98
- const addressType = decoded[0]
99
- const addressBody = decoded.slice(1)
100
-
101
- if (addressType == AddressType.P2PKH) {
102
- return groupOfP2pkhAddress(addressBody)
103
- } else if (addressType == AddressType.P2MPKH) {
104
- return groupOfP2mpkhAddress(addressBody)
105
- } else if (addressType == AddressType.P2SH) {
106
- return groupOfP2shAddress(addressBody)
107
- } else {
108
- // Contract Address
109
- const id = contractIdFromAddress(address)
110
- return id[`${id.length - 1}`]
111
- }
112
- }
113
-
114
- function groupOfAddressBytes(bytes: Uint8Array): number {
115
- const hint = djb2(bytes) | 1
116
- const hash = xorByte(hint)
117
- const group = hash % TOTAL_NUMBER_OF_GROUPS
118
- return group
119
- }
120
-
121
- // Pay to public key hash address
122
- function groupOfP2pkhAddress(address: Uint8Array): number {
123
- if (address.length != 32) {
124
- throw new Error(`Invalid p2pkh address length: ${address.length}`)
125
- }
126
-
127
- return groupOfAddressBytes(address)
128
- }
129
-
130
- // Pay to multiple public key hash address
131
- function groupOfP2mpkhAddress(address: Uint8Array): number {
132
- if ((address.length - 2) % 32 != 0) {
133
- throw new Error(`Invalid p2mpkh address length: ${address.length}`)
134
- }
135
-
136
- return groupOfAddressBytes(address.slice(1, 33))
137
- }
138
-
139
- // Pay to script hash address
140
- function groupOfP2shAddress(address: Uint8Array): number {
141
- return groupOfAddressBytes(address)
142
- }
143
-
144
- export function contractIdFromAddress(address: string): Uint8Array {
145
- return idFromAddress(address)
146
- }
147
-
148
- export function tokenIdFromAddress(address: string): Uint8Array {
149
- return idFromAddress(address)
150
- }
151
-
152
- function idFromAddress(address: string): Uint8Array {
153
- const decoded = bs58.decode(address)
154
-
155
- if (decoded.length == 0) throw new Error('Address string is empty')
156
- const addressType = decoded[0]
157
- const addressBody = decoded.slice(1)
158
-
159
- if (addressType == AddressType.P2C) {
160
- return addressBody
161
- } else {
162
- throw new Error(`Invalid contract address type: ${addressType}`)
163
- }
164
- }
165
-
166
77
  export function hexToBinUnsafe(hex: string): Uint8Array {
167
78
  return Buffer.from(hex, 'hex')
168
79
  }
@@ -171,72 +82,6 @@ export function binToHex(bin: Uint8Array): string {
171
82
  return Buffer.from(bin).toString('hex')
172
83
  }
173
84
 
174
- export function groupOfPrivateKey(privateKey: string, keyType?: KeyType): number {
175
- return groupOfAddress(addressFromPublicKey(publicKeyFromPrivateKey(privateKey, keyType), keyType))
176
- }
177
-
178
- export function publicKeyFromPrivateKey(privateKey: string, _keyType?: KeyType): string {
179
- const keyType = _keyType ?? 'default'
180
-
181
- if (keyType === 'default') {
182
- const key = ec.keyFromPrivate(privateKey)
183
- return key.getPublic(true, 'hex')
184
- } else {
185
- return ec.g.mul(new BN(privateKey, 16)).encode('hex', true).slice(2)
186
- }
187
- }
188
-
189
- export function addressFromPublicKey(publicKey: string, _keyType?: KeyType): string {
190
- const keyType = _keyType ?? 'default'
191
-
192
- if (keyType === 'default') {
193
- const addressType = Buffer.from([AddressType.P2PKH])
194
- const hash = Buffer.from(blake.blake2b(Buffer.from(publicKey, 'hex'), undefined, 32))
195
- const bytes = Buffer.concat([addressType, hash])
196
- return bs58.encode(bytes)
197
- } else {
198
- const lockupScript = Buffer.from(`0101000000000458144020${publicKey}8685`, 'hex')
199
- return addressFromScript(lockupScript)
200
- }
201
- }
202
-
203
- export function addressFromScript(script: Uint8Array): string {
204
- const scriptHash = blake.blake2b(script, undefined, 32)
205
- const addressType = Buffer.from([AddressType.P2SH])
206
- return bs58.encode(Buffer.concat([addressType, scriptHash]))
207
- }
208
-
209
- export function addressFromContractId(contractId: string): string {
210
- const addressType = Buffer.from([AddressType.P2C])
211
- const hash = Buffer.from(hexToBinUnsafe(contractId))
212
- const bytes = Buffer.concat([addressType, hash])
213
- return bs58.encode(bytes)
214
- }
215
-
216
- export function addressFromTokenId(tokenId: string): string {
217
- const contractId = tokenId // contract ID is the same as token ID
218
- return addressFromContractId(contractId)
219
- }
220
-
221
- export function contractIdFromTx(txId: string, outputIndex: number): string {
222
- const txIdBin = hexToBinUnsafe(txId)
223
- const data = Buffer.concat([txIdBin, Buffer.from([outputIndex])])
224
- const hash = blake.blake2b(data, undefined, 32)
225
- return binToHex(hash)
226
- }
227
-
228
- export function subContractId(parentContractId: string, pathInHex: string, group: number): string {
229
- if (group < 0 || group >= TOTAL_NUMBER_OF_GROUPS) {
230
- throw new Error(`Invalid group ${group}`)
231
- }
232
- const data = Buffer.concat([hexToBinUnsafe(parentContractId), hexToBinUnsafe(pathInHex)])
233
- const bytes = Buffer.concat([
234
- blake.blake2b(blake.blake2b(data, undefined, 32), undefined, 32).slice(0, -1),
235
- Buffer.from([group])
236
- ])
237
- return binToHex(bytes)
238
- }
239
-
240
85
  export function blockChainIndex(blockHash: HexString): { fromGroup: number; toGroup: number } {
241
86
  if (blockHash.length != 64) {
242
87
  throw Error(`Invalid block hash: ${blockHash}`)