@alephium/web3 0.5.0-rc.1 → 0.5.0-rc.10

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.
Files changed (39) hide show
  1. package/dist/alephium-web3.min.js +1 -1
  2. package/dist/alephium-web3.min.js.LICENSE.txt +2 -0
  3. package/dist/alephium-web3.min.js.map +1 -1
  4. package/dist/src/api/api-alephium.d.ts +57 -20
  5. package/dist/src/api/api-alephium.js +57 -15
  6. package/dist/src/api/index.d.ts +1 -0
  7. package/dist/src/api/index.js +1 -0
  8. package/dist/src/api/types.d.ts +1 -1
  9. package/dist/src/api/types.js +6 -2
  10. package/dist/src/constants.d.ts +1 -0
  11. package/dist/src/constants.js +2 -1
  12. package/dist/src/contract/contract.d.ts +9 -4
  13. package/dist/src/contract/contract.js +31 -14
  14. package/dist/src/signer/signer.d.ts +29 -30
  15. package/dist/src/signer/signer.js +34 -25
  16. package/dist/src/signer/tx-builder.d.ts +2 -7
  17. package/dist/src/signer/tx-builder.js +10 -7
  18. package/dist/src/signer/types.d.ts +8 -0
  19. package/dist/src/transaction/sign-verify.d.ts +3 -2
  20. package/dist/src/transaction/sign-verify.js +4 -14
  21. package/dist/src/utils/index.d.ts +1 -0
  22. package/dist/src/utils/index.js +1 -0
  23. package/dist/src/utils/sign.d.ts +3 -0
  24. package/dist/src/utils/sign.js +89 -0
  25. package/dist/src/utils/utils.d.ts +4 -3
  26. package/dist/src/utils/utils.js +25 -10
  27. package/package.json +2 -1
  28. package/src/api/api-alephium.ts +88 -32
  29. package/src/api/index.ts +2 -0
  30. package/src/api/types.ts +12 -2
  31. package/src/constants.ts +1 -0
  32. package/src/contract/contract.ts +49 -20
  33. package/src/signer/signer.ts +69 -55
  34. package/src/signer/tx-builder.ts +13 -7
  35. package/src/signer/types.ts +10 -2
  36. package/src/transaction/sign-verify.ts +10 -15
  37. package/src/utils/index.ts +1 -0
  38. package/src/utils/sign.ts +66 -0
  39. package/src/utils/utils.ts +26 -10
@@ -848,38 +848,45 @@ export class Contract extends Artifact {
848
848
  }
849
849
 
850
850
  static ContractCreatedEventIndex = -1
851
- static ContractCreatedEvent: EventSig = {
851
+ static ContractCreatedEvent: SystemEventSig = {
852
852
  name: 'ContractCreated',
853
853
  fieldNames: ['address'],
854
- fieldTypes: ['Address']
854
+ fieldTypes: ['Address'],
855
+ optionalFieldNames: ['parentAddress'],
856
+ optionalFieldTypes: ['Address']
855
857
  }
856
858
 
857
859
  static ContractDestroyedEventIndex = -2
858
- static ContractDestroyedEvent: EventSig = {
860
+ static ContractDestroyedEvent: SystemEventSig = {
859
861
  name: 'ContractDestroyed',
860
862
  fieldNames: ['address'],
861
863
  fieldTypes: ['Address']
862
864
  }
863
865
 
864
866
  static fromApiEvent(event: node.ContractEventByTxId, codeHash: string | undefined, txId: string): ContractEvent {
865
- let eventSig: EventSig
867
+ let fields: Fields
868
+ let name: string
866
869
 
867
870
  if (event.eventIndex == Contract.ContractCreatedEventIndex) {
868
- eventSig = this.ContractCreatedEvent
871
+ fields = fromApiSystemEventFields(event.fields, Contract.ContractCreatedEvent)
872
+ name = Contract.ContractCreatedEvent.name
869
873
  } else if (event.eventIndex == Contract.ContractDestroyedEventIndex) {
870
- eventSig = this.ContractDestroyedEvent
874
+ fields = fromApiSystemEventFields(event.fields, Contract.ContractDestroyedEvent)
875
+ name = Contract.ContractDestroyedEvent.name
871
876
  } else {
872
877
  const contract = Project.currentProject.contractByCodeHash(codeHash!)
873
- eventSig = contract.eventsSig[event.eventIndex]
878
+ const eventSig = contract.eventsSig[event.eventIndex]
879
+ fields = fromApiEventFields(event.fields, eventSig)
880
+ name = eventSig.name
874
881
  }
875
882
 
876
883
  return {
877
884
  txId: txId,
878
885
  blockHash: event.blockHash,
879
886
  contractAddress: event.contractAddress,
880
- name: eventSig.name,
887
+ name: name,
881
888
  eventIndex: event.eventIndex,
882
- fields: fromApiEventFields(event.fields, eventSig)
889
+ fields: fields
883
890
  }
884
891
  }
885
892
 
@@ -913,8 +920,10 @@ export class Contract extends Artifact {
913
920
  params: DeployContractParams<P>
914
921
  ): Promise<SignDeployContractTxParams> {
915
922
  const bytecode = this.buildByteCodeToDeploy(params.initialFields ?? {})
923
+ const selectedAccount = await signer.getSelectedAccount()
916
924
  const signerParams: SignDeployContractTxParams = {
917
- signerAddress: await signer.getSelectedAddress(),
925
+ signerAddress: selectedAccount.address,
926
+ signerKeyType: selectedAccount.keyType,
918
927
  bytecode: bytecode,
919
928
  initialAttoAlphAmount: params?.initialAttoAlphAmount,
920
929
  issueTokenAmount: params?.issueTokenAmount,
@@ -1056,8 +1065,10 @@ export class Script extends Artifact {
1056
1065
  signer: SignerProvider,
1057
1066
  params: ExecuteScriptParams<P>
1058
1067
  ): Promise<SignExecuteScriptTxParams> {
1068
+ const selectedAccount = await signer.getSelectedAccount()
1059
1069
  const signerParams: SignExecuteScriptTxParams = {
1060
- signerAddress: await signer.getSelectedAddress(),
1070
+ signerAddress: selectedAccount.address,
1071
+ signerKeyType: selectedAccount.keyType,
1061
1072
  bytecode: this.buildByteCodeToDeploy(params.initialFields ?? {}),
1062
1073
  attoAlphAmount: params.attoAlphAmount,
1063
1074
  tokens: params.tokens,
@@ -1095,6 +1106,16 @@ function fromApiEventFields(vals: node.Val[], eventSig: node.EventSig): Fields {
1095
1106
  return fromApiVals(vals, eventSig.fieldNames, eventSig.fieldTypes)
1096
1107
  }
1097
1108
 
1109
+ function fromApiSystemEventFields(vals: node.Val[], systemEventSig: SystemEventSig): Fields {
1110
+ return fromApiVals(
1111
+ vals,
1112
+ systemEventSig.fieldNames,
1113
+ systemEventSig.fieldTypes,
1114
+ systemEventSig.optionalFieldNames ?? [],
1115
+ systemEventSig.optionalFieldTypes ?? []
1116
+ )
1117
+ }
1118
+
1098
1119
  export interface Asset {
1099
1120
  alphAmount: Number256
1100
1121
  tokens?: Token[]
@@ -1272,7 +1293,7 @@ export interface DeployContractParams<P extends Fields = Fields> {
1272
1293
  assertType<
1273
1294
  Eq<
1274
1295
  Omit<DeployContractParams<undefined>, 'initialFields'>,
1275
- Omit<SignDeployContractTxParams, 'signerAddress' | 'bytecode'>
1296
+ Omit<SignDeployContractTxParams, 'signerAddress' | 'signerKeyType' | 'bytecode'>
1276
1297
  >
1277
1298
  >
1278
1299
  export type DeployContractResult<T> = SignDeployContractTxResult & { instance: T }
@@ -1339,37 +1360,45 @@ export interface CallContractResult<R> {
1339
1360
  events: ContractEvent[]
1340
1361
  }
1341
1362
 
1342
- export type ContractCreatedEvent = ContractEvent<{ address: HexString }>
1343
- export type ContractDestroyedEvent = ContractEvent<{ address: HexString }>
1363
+ export interface SystemEventSig extends EventSig {
1364
+ optionalFieldNames?: string[]
1365
+ optionalFieldTypes?: string[]
1366
+ }
1367
+
1368
+ export type ContractCreatedEvent = ContractEvent<{ address: Address; parentAddress?: Address }>
1369
+ export type ContractDestroyedEvent = ContractEvent<{ address: Address }>
1344
1370
 
1345
- function decodeFields(event: node.ContractEvent, eventSig: EventSig, eventIndex: number): Fields {
1371
+ function decodeSystemEvent(event: node.ContractEvent, systemEventSig: SystemEventSig, eventIndex: number): Fields {
1346
1372
  if (event.eventIndex !== eventIndex) {
1347
1373
  throw new Error(`Invalid event index: ${event.eventIndex}, expected: ${eventIndex}`)
1348
1374
  }
1349
- return fromApiVals(event.fields, eventSig.fieldNames, eventSig.fieldTypes)
1375
+ return fromApiSystemEventFields(event.fields, systemEventSig)
1350
1376
  }
1351
1377
 
1352
1378
  export function decodeContractCreatedEvent(event: node.ContractEvent): Omit<ContractCreatedEvent, 'contractAddress'> {
1353
- const fields = decodeFields(event, Contract.ContractCreatedEvent, Contract.ContractCreatedEventIndex)
1379
+ const fields = decodeSystemEvent(event, Contract.ContractCreatedEvent, Contract.ContractCreatedEventIndex)
1354
1380
  return {
1355
1381
  blockHash: event.blockHash,
1356
1382
  txId: event.txId,
1357
1383
  eventIndex: event.eventIndex,
1358
1384
  name: Contract.ContractCreatedEvent.name,
1359
- fields: { address: fields['address'] as HexString }
1385
+ fields: {
1386
+ address: fields['address'] as Address,
1387
+ parentAddress: fields['parentAddress'] === undefined ? undefined : (fields['parentAddress'] as Address)
1388
+ }
1360
1389
  }
1361
1390
  }
1362
1391
 
1363
1392
  export function decodeContractDestroyedEvent(
1364
1393
  event: node.ContractEvent
1365
1394
  ): Omit<ContractDestroyedEvent, 'contractAddress'> {
1366
- const fields = decodeFields(event, Contract.ContractDestroyedEvent, Contract.ContractDestroyedEventIndex)
1395
+ const fields = decodeSystemEvent(event, Contract.ContractDestroyedEvent, Contract.ContractDestroyedEventIndex)
1367
1396
  return {
1368
1397
  blockHash: event.blockHash,
1369
1398
  txId: event.txId,
1370
1399
  eventIndex: event.eventIndex,
1371
1400
  name: Contract.ContractDestroyedEvent.name,
1372
- fields: { address: fields['address'] as HexString }
1401
+ fields: { address: fields['address'] as Address }
1373
1402
  }
1374
1403
  }
1375
1404
 
@@ -16,7 +16,6 @@ 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
19
  import { ExplorerProvider, fromApiNumber256, fromApiTokens, NodeProvider, toApiNumber256, toApiTokens } from '../api'
21
20
  import { node } from '../api'
22
21
  import * as utils from '../utils'
@@ -43,53 +42,70 @@ import {
43
42
  ExtSignDeployContractTxParams,
44
43
  ExtSignExecuteScriptTxParams,
45
44
  ExtSignUnsignedTxParams,
46
- ExtSignMessageParams
45
+ ExtSignMessageParams,
46
+ KeyType
47
47
  } from './types'
48
48
  import { TransactionBuilder } from './tx-builder'
49
+ import { addressFromPublicKey, groupOfAddress } from '../utils'
49
50
 
50
- const ec = new EC('secp256k1')
51
+ export abstract class SignerProvider {
52
+ abstract get nodeProvider(): NodeProvider | undefined
53
+ abstract get explorerProvider(): ExplorerProvider | undefined
51
54
 
52
- export interface SignerProvider {
53
- get nodeProvider(): NodeProvider | undefined
54
- get explorerProvider(): ExplorerProvider | undefined
55
+ protected abstract unsafeGetSelectedAccount(): Promise<Account>
56
+ async getSelectedAccount(): Promise<Account> {
57
+ const account = await this.unsafeGetSelectedAccount()
58
+ SignerProvider.validateAccount(account)
59
+ return account
60
+ }
55
61
 
56
- getSelectedAddress(): Promise<Address>
62
+ static validateAccount(account: Account): void {
63
+ const derivedAddress = addressFromPublicKey(account.publicKey, account.keyType)
64
+ const derivedGroup = groupOfAddress(derivedAddress)
65
+ if (derivedAddress !== account.address || derivedGroup !== account.group) {
66
+ throw Error(`Invalid accounot data: ${JSON.stringify(account)}`)
67
+ }
68
+ }
57
69
 
58
- signAndSubmitTransferTx(params: SignTransferTxParams): Promise<SignTransferTxResult>
59
- signAndSubmitDeployContractTx(params: SignDeployContractTxParams): Promise<SignDeployContractTxResult>
60
- signAndSubmitExecuteScriptTx(params: SignExecuteScriptTxParams): Promise<SignExecuteScriptTxResult>
61
- signAndSubmitUnsignedTx(params: SignUnsignedTxParams): Promise<SignUnsignedTxResult>
70
+ abstract signAndSubmitTransferTx(params: SignTransferTxParams): Promise<SignTransferTxResult>
71
+ abstract signAndSubmitDeployContractTx(params: SignDeployContractTxParams): Promise<SignDeployContractTxResult>
72
+ abstract signAndSubmitExecuteScriptTx(params: SignExecuteScriptTxParams): Promise<SignExecuteScriptTxResult>
73
+ abstract signAndSubmitUnsignedTx(params: SignUnsignedTxParams): Promise<SignUnsignedTxResult>
62
74
 
63
- signUnsignedTx(params: SignUnsignedTxParams): Promise<SignUnsignedTxResult>
75
+ abstract signUnsignedTx(params: SignUnsignedTxParams): Promise<SignUnsignedTxResult>
64
76
  // The message will be prefixed with 'Alephium Signed Message: ' before signing
65
77
  // so that the resulted signature cannot be reused for building transactions.
66
- signMessage(params: SignMessageParams): Promise<SignMessageResult>
78
+ abstract signMessage(params: SignMessageParams): Promise<SignMessageResult>
67
79
  }
68
80
 
69
81
  // Abstraction for interactive signer (e.g. WalletConnect instance, Extension wallet object)
70
- export interface InteractiveSignerProvider<EnableOptions extends EnableOptionsBase = EnableOptionsBase>
71
- extends SignerProvider {
72
- enable(opt?: EnableOptions): Promise<Address>
73
- disconnect(): Promise<void>
82
+ export abstract class InteractiveSignerProvider<
83
+ EnableOptions extends EnableOptionsBase = EnableOptionsBase
84
+ > extends SignerProvider {
85
+ protected abstract unsafeEnable(opt?: EnableOptions): Promise<Account>
86
+ async enable(opt?: EnableOptions): Promise<Account> {
87
+ const account = await this.unsafeEnable(opt)
88
+ SignerProvider.validateAccount(account)
89
+ return account
90
+ }
91
+
92
+ abstract disconnect(): Promise<void>
74
93
 
75
94
  // Methods inherited from SignerProvider, but require networkId in the params
76
- signAndSubmitTransferTx(params: ExtSignTransferTxParams): Promise<SignTransferTxResult>
77
- signAndSubmitDeployContractTx(params: ExtSignDeployContractTxParams): Promise<SignDeployContractTxResult>
78
- signAndSubmitExecuteScriptTx(params: ExtSignExecuteScriptTxParams): Promise<SignExecuteScriptTxResult>
79
- signAndSubmitUnsignedTx(params: ExtSignUnsignedTxParams): Promise<SignUnsignedTxResult>
80
- signUnsignedTx(params: ExtSignUnsignedTxParams): Promise<SignUnsignedTxResult>
81
- signMessage(params: ExtSignMessageParams): Promise<SignMessageResult>
95
+ abstract override signAndSubmitTransferTx(params: ExtSignTransferTxParams): Promise<SignTransferTxResult>
96
+ abstract override signAndSubmitDeployContractTx(
97
+ params: ExtSignDeployContractTxParams
98
+ ): Promise<SignDeployContractTxResult>
99
+ abstract override signAndSubmitExecuteScriptTx(
100
+ params: ExtSignExecuteScriptTxParams
101
+ ): Promise<SignExecuteScriptTxResult>
102
+ abstract override signAndSubmitUnsignedTx(params: ExtSignUnsignedTxParams): Promise<SignUnsignedTxResult>
103
+ abstract override signUnsignedTx(params: ExtSignUnsignedTxParams): Promise<SignUnsignedTxResult>
104
+ abstract override signMessage(params: ExtSignMessageParams): Promise<SignMessageResult>
82
105
  }
83
106
 
84
- export abstract class SignerProviderSimple extends TransactionBuilder implements SignerProvider {
85
- abstract get explorerProvider(): ExplorerProvider | undefined
86
-
87
- abstract getSelectedAccount(): Promise<Account>
88
-
89
- async getSelectedAddress(): Promise<Address> {
90
- const account = await this.getSelectedAccount()
91
- return account.address
92
- }
107
+ export abstract class SignerProviderSimple extends SignerProvider {
108
+ abstract override get nodeProvider(): NodeProvider
93
109
 
94
110
  async submitTransaction(params: SubmitTransactionParams): Promise<SubmissionResult> {
95
111
  const data: node.SubmitTransaction = { unsignedTx: params.unsignedTx, signature: params.signature }
@@ -133,8 +149,11 @@ export abstract class SignerProviderSimple extends TransactionBuilder implements
133
149
  return { signature, ...response }
134
150
  }
135
151
 
136
- override async buildTransferTx(params: SignTransferTxParams): Promise<Omit<SignTransferTxResult, 'signature'>> {
137
- return super.buildTransferTx(params, await this.getPublicKey(params.signerAddress))
152
+ async buildTransferTx(params: SignTransferTxParams): Promise<Omit<SignTransferTxResult, 'signature'>> {
153
+ return TransactionBuilder.from(this.nodeProvider).buildTransferTx(
154
+ params,
155
+ await this.getPublicKey(params.signerAddress)
156
+ )
138
157
  }
139
158
 
140
159
  async signDeployContractTx(params: SignDeployContractTxParams): Promise<SignDeployContractTxResult> {
@@ -143,10 +162,13 @@ export abstract class SignerProviderSimple extends TransactionBuilder implements
143
162
  return { signature, ...response }
144
163
  }
145
164
 
146
- override async buildDeployContractTx(
165
+ async buildDeployContractTx(
147
166
  params: SignDeployContractTxParams
148
167
  ): Promise<Omit<SignDeployContractTxResult, 'signature'>> {
149
- return super.buildDeployContractTx(params, await this.getPublicKey(params.signerAddress))
168
+ return TransactionBuilder.from(this.nodeProvider).buildDeployContractTx(
169
+ params,
170
+ await this.getPublicKey(params.signerAddress)
171
+ )
150
172
  }
151
173
 
152
174
  async signExecuteScriptTx(params: SignExecuteScriptTxParams): Promise<SignExecuteScriptTxResult> {
@@ -155,16 +177,17 @@ export abstract class SignerProviderSimple extends TransactionBuilder implements
155
177
  return { signature, ...response }
156
178
  }
157
179
 
158
- override async buildExecuteScriptTx(
159
- params: SignExecuteScriptTxParams
160
- ): Promise<Omit<SignExecuteScriptTxResult, 'signature'>> {
161
- return super.buildExecuteScriptTx(params, await this.getPublicKey(params.signerAddress))
180
+ async buildExecuteScriptTx(params: SignExecuteScriptTxParams): Promise<Omit<SignExecuteScriptTxResult, 'signature'>> {
181
+ return TransactionBuilder.from(this.nodeProvider).buildExecuteScriptTx(
182
+ params,
183
+ await this.getPublicKey(params.signerAddress)
184
+ )
162
185
  }
163
186
 
164
187
  // in general, wallet should show the decoded information to user for confirmation
165
188
  // please overwrite this function for real wallet
166
189
  async signUnsignedTx(params: SignUnsignedTxParams): Promise<SignUnsignedTxResult> {
167
- const response = await this.buildUnsignedTx(params)
190
+ const response = await TransactionBuilder.from(this.nodeProvider).buildUnsignedTx(params)
168
191
  const signature = await this.signRaw(params.signerAddress, response.txId)
169
192
  return { signature, ...response }
170
193
  }
@@ -180,7 +203,7 @@ export abstract class SignerProviderSimple extends TransactionBuilder implements
180
203
  }
181
204
 
182
205
  export abstract class SignerProviderWithMultipleAccounts extends SignerProviderSimple {
183
- abstract setSelectedAddress(address: string): Promise<void>
206
+ abstract setSelectedAccount(address: string): Promise<void>
184
207
 
185
208
  abstract getAccounts(): Promise<Account[]>
186
209
 
@@ -204,7 +227,7 @@ export abstract class SignerProviderWithCachedAccounts<T extends Account> extend
204
227
  private _selectedAccount: T | undefined = undefined
205
228
  protected readonly _accounts = new Map<Address, T>()
206
229
 
207
- getSelectedAccount(): Promise<T> {
230
+ protected unsafeGetSelectedAccount(): Promise<T> {
208
231
  if (this._selectedAccount === undefined) {
209
232
  throw Error('No account is selected yet')
210
233
  } else {
@@ -212,7 +235,7 @@ export abstract class SignerProviderWithCachedAccounts<T extends Account> extend
212
235
  }
213
236
  }
214
237
 
215
- setSelectedAddress(address: string): Promise<void> {
238
+ setSelectedAccount(address: string): Promise<void> {
216
239
  const accountOpt = this._accounts.get(address)
217
240
  if (accountOpt === undefined) {
218
241
  throw Error('The address is not in the accounts')
@@ -236,23 +259,14 @@ export abstract class SignerProviderWithCachedAccounts<T extends Account> extend
236
259
  }
237
260
  }
238
261
 
239
- export function verifyHexString(hexString: string, publicKey: string, signature: string): boolean {
240
- try {
241
- const key = ec.keyFromPublic(publicKey, 'hex')
242
- return key.verify(hexString, utils.signatureDecode(ec, signature))
243
- } catch (error) {
244
- return false
245
- }
246
- }
247
-
248
262
  function extendMessage(message: string): string {
249
263
  return 'Alephium Signed Message: ' + message
250
264
  }
251
265
 
252
- export function verifySignedMessage(message: string, publicKey: string, signature: string): boolean {
266
+ export function verifySignedMessage(message: string, publicKey: string, signature: string, keyType: KeyType): boolean {
253
267
  const extendedMessage = extendMessage(message)
254
268
  const messageHash = blake.blake2b(extendedMessage, undefined, 32)
255
- return verifyHexString(utils.binToHex(messageHash), publicKey, signature)
269
+ return utils.verifySignature(utils.binToHex(messageHash), publicKey, signature, keyType)
256
270
  }
257
271
 
258
272
  export function toApiDestination(data: Destination): node.Destination {
@@ -21,6 +21,7 @@ import { fromApiNumber256, node, NodeProvider, toApiNumber256Optional, toApiToke
21
21
  import { addressFromPublicKey } from '../utils'
22
22
  import { toApiDestinations } from './signer'
23
23
  import {
24
+ KeyType,
24
25
  SignDeployContractTxParams,
25
26
  SignDeployContractTxResult,
26
27
  SignerAddress,
@@ -35,8 +36,10 @@ import {
35
36
  export abstract class TransactionBuilder {
36
37
  abstract get nodeProvider(): NodeProvider
37
38
 
38
- static create(baseUrl: string, apiKey?: string) {
39
- const nodeProvider = new NodeProvider(baseUrl, apiKey)
39
+ static from(nodeProvider: NodeProvider): TransactionBuilder
40
+ static from(baseUrl: string, apiKey?: string): TransactionBuilder
41
+ static from(param0: string | NodeProvider, param1?: string): TransactionBuilder {
42
+ const nodeProvider = typeof param0 === 'string' ? new NodeProvider(param0, param1) : (param0 as NodeProvider)
40
43
  return new (class extends TransactionBuilder {
41
44
  get nodeProvider(): NodeProvider {
42
45
  return nodeProvider
@@ -44,8 +47,8 @@ export abstract class TransactionBuilder {
44
47
  })()
45
48
  }
46
49
 
47
- private static validatePublicKey(params: SignerAddress, publicKey: string) {
48
- const address = addressFromPublicKey(publicKey)
50
+ private static validatePublicKey(params: SignerAddress, publicKey: string, keyType: KeyType) {
51
+ const address = addressFromPublicKey(publicKey, keyType)
49
52
  if (address !== params.signerAddress) {
50
53
  throw new Error('Unmatched public key')
51
54
  }
@@ -55,11 +58,12 @@ export abstract class TransactionBuilder {
55
58
  params: SignTransferTxParams,
56
59
  publicKey: string
57
60
  ): Promise<Omit<SignTransferTxResult, 'signature'>> {
58
- TransactionBuilder.validatePublicKey(params, publicKey)
61
+ TransactionBuilder.validatePublicKey(params, publicKey, params.signerKeyType ?? 'default')
59
62
 
60
63
  const { destinations, gasPrice, ...rest } = params
61
64
  const data: node.BuildTransaction = {
62
65
  fromPublicKey: publicKey,
66
+ fromPublicKeyType: params.signerKeyType,
63
67
  destinations: toApiDestinations(destinations),
64
68
  gasPrice: toApiNumber256Optional(gasPrice),
65
69
  ...rest
@@ -72,11 +76,12 @@ export abstract class TransactionBuilder {
72
76
  params: SignDeployContractTxParams,
73
77
  publicKey: string
74
78
  ): Promise<Omit<SignDeployContractTxResult, 'signature'>> {
75
- TransactionBuilder.validatePublicKey(params, publicKey)
79
+ TransactionBuilder.validatePublicKey(params, publicKey, params.signerKeyType ?? 'default')
76
80
 
77
81
  const { initialAttoAlphAmount, initialTokenAmounts, issueTokenAmount, gasPrice, ...rest } = params
78
82
  const data: node.BuildDeployContractTx = {
79
83
  fromPublicKey: publicKey,
84
+ fromPublicKeyType: params.signerKeyType,
80
85
  initialAttoAlphAmount: toApiNumber256Optional(initialAttoAlphAmount),
81
86
  initialTokenAmounts: toApiTokens(initialTokenAmounts),
82
87
  issueTokenAmount: toApiNumber256Optional(issueTokenAmount),
@@ -92,11 +97,12 @@ export abstract class TransactionBuilder {
92
97
  params: SignExecuteScriptTxParams,
93
98
  publicKey: string
94
99
  ): Promise<Omit<SignExecuteScriptTxResult, 'signature'>> {
95
- TransactionBuilder.validatePublicKey(params, publicKey)
100
+ TransactionBuilder.validatePublicKey(params, publicKey, params.signerKeyType ?? 'default')
96
101
 
97
102
  const { attoAlphAmount, tokens, gasPrice, ...rest } = params
98
103
  const data: node.BuildExecuteScriptTx = {
99
104
  fromPublicKey: publicKey,
105
+ fromPublicKeyType: params.signerKeyType,
100
106
  attoAlphAmount: toApiNumber256Optional(attoAlphAmount),
101
107
  tokens: toApiTokens(tokens),
102
108
  gasPrice: toApiNumber256Optional(gasPrice),
@@ -33,18 +33,22 @@ export interface Destination {
33
33
  }
34
34
  assertType<Eq<keyof Destination, keyof node.Destination>>
35
35
 
36
+ export type KeyType = 'default' | 'bip340-schnorr'
37
+
36
38
  export interface Account {
39
+ keyType: KeyType
37
40
  address: string
38
41
  group: number
39
42
  publicKey: string
40
43
  }
41
44
 
42
- export type SignerAddress = { signerAddress: string }
43
- type TxBuildParams<T> = Omit<T, 'fromPublicKey' | 'targetBlockHash'> & SignerAddress
45
+ export type SignerAddress = { signerAddress: string; signerKeyType?: KeyType }
46
+ type TxBuildParams<T> = Omit<T, 'fromPublicKey' | 'fromPublicKeyType' | 'targetBlockHash'> & SignerAddress
44
47
  type SignResult<T> = Omit<T, 'gasPrice'> & { signature: string; gasPrice: Number256 }
45
48
 
46
49
  export interface SignTransferTxParams {
47
50
  signerAddress: string
51
+ signerKeyType?: KeyType
48
52
  destinations: Destination[]
49
53
  utxos?: OutputRef[]
50
54
  gasAmount?: number
@@ -64,6 +68,7 @@ assertType<Eq<SignTransferTxResult, SignResult<node.BuildTransactionResult>>>()
64
68
 
65
69
  export interface SignDeployContractTxParams {
66
70
  signerAddress: string
71
+ signerKeyType?: KeyType
67
72
  bytecode: string
68
73
  initialAttoAlphAmount?: Number256
69
74
  initialTokenAmounts?: Token[]
@@ -91,6 +96,7 @@ assertType<
91
96
 
92
97
  export interface SignExecuteScriptTxParams {
93
98
  signerAddress: string
99
+ signerKeyType?: KeyType
94
100
  bytecode: string
95
101
  attoAlphAmount?: Number256
96
102
  tokens?: Token[]
@@ -115,6 +121,7 @@ assertType<
115
121
 
116
122
  export interface SignUnsignedTxParams {
117
123
  signerAddress: string
124
+ signerKeyType?: KeyType
118
125
  unsignedTx: string
119
126
  }
120
127
  assertType<Eq<SignUnsignedTxParams, { unsignedTx: string } & SignerAddress>>()
@@ -131,6 +138,7 @@ assertType<Eq<SignUnsignedTxResult, SignTransferTxResult>>
131
138
 
132
139
  export interface SignMessageParams {
133
140
  signerAddress: string
141
+ signerKeyType?: KeyType
134
142
  message: string
135
143
  }
136
144
  assertType<Eq<SignMessageParams, { message: string } & SignerAddress>>()
@@ -17,22 +17,17 @@ along with the library. If not, see <http://www.gnu.org/licenses/>.
17
17
  */
18
18
 
19
19
  import * as utils from '../utils'
20
- import { ec as EC } from 'elliptic'
20
+ import { KeyType } from '../signer'
21
21
 
22
- const ec = new EC('secp256k1')
23
-
24
- export function transactionSign(txHash: string, privateKey: string): string {
25
- const keyPair = ec.keyFromPrivate(privateKey)
26
- const signature = keyPair.sign(txHash)
27
-
28
- return utils.encodeSignature(signature)
22
+ export function transactionSign(txId: string, privateKey: string, keyType: KeyType): string {
23
+ return utils.sign(txId, privateKey, keyType)
29
24
  }
30
25
 
31
- export function transactionVerifySignature(txHash: string, publicKey: string, signature: string): boolean {
32
- try {
33
- const key = ec.keyFromPublic(publicKey, 'hex')
34
- return key.verify(txHash, utils.signatureDecode(ec, signature))
35
- } catch (error) {
36
- return false
37
- }
26
+ export function transactionVerifySignature(
27
+ txId: string,
28
+ publicKey: string,
29
+ signature: string,
30
+ keyType: KeyType
31
+ ): boolean {
32
+ return utils.verifySignature(txId, publicKey, signature, keyType)
38
33
  }
@@ -21,3 +21,4 @@ export * from './bs58'
21
21
  export * from './djb2'
22
22
  export * from './utils'
23
23
  export * from './subscription'
24
+ export * from './sign'
@@ -0,0 +1,66 @@
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
+ import { ec as EC } from 'elliptic'
20
+ import { binToHex, encodeSignature, hexToBinUnsafe, signatureDecode } from '..'
21
+ import { KeyType } from '../signer'
22
+ import * as necc from '@noble/secp256k1'
23
+ import { createHash, createHmac } from 'crypto'
24
+
25
+ const ec = new EC('secp256k1')
26
+
27
+ necc.utils.sha256Sync = (...messages: Uint8Array[]): Uint8Array => {
28
+ const sha256 = createHash('sha256')
29
+ for (const message of messages) sha256.update(message)
30
+ return sha256.digest()
31
+ }
32
+
33
+ necc.utils.hmacSha256Sync = (key: Uint8Array, ...messages: Uint8Array[]): Uint8Array => {
34
+ const hash = createHmac('sha256', Buffer.from(key))
35
+ messages.forEach((m) => hash.update(m))
36
+ return Uint8Array.from(hash.digest())
37
+ }
38
+
39
+ // hash has to be 32 bytes
40
+ export function sign(hash: string, privateKey: string, _keyType: KeyType): string {
41
+ const keyType = _keyType ?? 'default'
42
+
43
+ if (keyType === 'default') {
44
+ const key = ec.keyFromPrivate(privateKey)
45
+ const signature = key.sign(hash)
46
+ return encodeSignature(signature)
47
+ } else {
48
+ const signature = necc.schnorr.signSync(hexToBinUnsafe(hash), hexToBinUnsafe(privateKey))
49
+ return binToHex(signature)
50
+ }
51
+ }
52
+
53
+ export function verifySignature(hash: string, publicKey: string, signature: string, _keyType: KeyType): boolean {
54
+ const keyType = _keyType ?? 'default'
55
+
56
+ try {
57
+ if (keyType === 'default') {
58
+ const key = ec.keyFromPublic(publicKey, 'hex')
59
+ return key.verify(hash, signatureDecode(ec, signature))
60
+ } else {
61
+ return necc.schnorr.verifySync(hexToBinUnsafe(signature), hexToBinUnsafe(hash), hexToBinUnsafe(publicKey))
62
+ }
63
+ } catch (error) {
64
+ return false
65
+ }
66
+ }
@@ -24,6 +24,7 @@ import { Buffer } from 'buffer/'
24
24
 
25
25
  import { TOTAL_NUMBER_OF_GROUPS } from '../constants'
26
26
  import djb2 from './djb2'
27
+ import { KeyType } from '../signer'
27
28
 
28
29
  const ec = new EC('secp256k1')
29
30
 
@@ -157,20 +158,35 @@ export function binToHex(bin: Uint8Array): string {
157
158
  return Buffer.from(bin).toString('hex')
158
159
  }
159
160
 
160
- export function groupOfPrivateKey(privateKey: string): number {
161
- return groupOfAddress(addressFromPublicKey(publicKeyFromPrivateKey(privateKey)))
161
+ export function groupOfPrivateKey(privateKey: string, keyType: KeyType): number {
162
+ return groupOfAddress(addressFromPublicKey(publicKeyFromPrivateKey(privateKey, keyType), keyType))
162
163
  }
163
164
 
164
- export function publicKeyFromPrivateKey(privateKey: string): string {
165
- const key = ec.keyFromPrivate(privateKey)
166
- return key.getPublic(true, 'hex')
165
+ export function publicKeyFromPrivateKey(privateKey: string, _keyType: KeyType): string {
166
+ const keyType = _keyType ?? 'default'
167
+
168
+ if (keyType === 'default') {
169
+ const key = ec.keyFromPrivate(privateKey)
170
+ return key.getPublic(true, 'hex')
171
+ } else {
172
+ return ec.g.mul(new BN(privateKey, 16)).encode('hex', true).slice(2)
173
+ }
167
174
  }
168
175
 
169
- export function addressFromPublicKey(publicKey: string): string {
170
- const addressType = Buffer.from([AddressType.P2PKH])
171
- const hash = Buffer.from(blake.blake2b(Buffer.from(publicKey, 'hex'), undefined, 32))
172
- const bytes = Buffer.concat([addressType, hash])
173
- return bs58.encode(bytes)
176
+ export function addressFromPublicKey(publicKey: string, _keyType: KeyType): string {
177
+ const keyType = _keyType ?? 'default'
178
+
179
+ if (keyType === 'default') {
180
+ const addressType = Buffer.from([AddressType.P2PKH])
181
+ const hash = Buffer.from(blake.blake2b(Buffer.from(publicKey, 'hex'), undefined, 32))
182
+ const bytes = Buffer.concat([addressType, hash])
183
+ return bs58.encode(bytes)
184
+ } else {
185
+ const lockupScript = Buffer.from(`0101000000000458144020${publicKey}8685`, 'hex')
186
+ const lockupScriptHash = blake.blake2b(lockupScript, undefined, 32)
187
+ const addressType = Buffer.from([AddressType.P2SH])
188
+ return bs58.encode(Buffer.concat([addressType, lockupScriptHash]))
189
+ }
174
190
  }
175
191
 
176
192
  export function addressFromContractId(contractId: string): string {