From b76c1e2797ca66f72567f8f766207fec2f86509f Mon Sep 17 00:00:00 2001 From: lbqds Date: Thu, 4 Jul 2024 19:44:24 +0800 Subject: [PATCH 1/4] Generate random contract id by group index --- packages/web3-test/src/test-wallet.test.ts | 31 ++++++++++++++++++++++ packages/web3-test/src/test-wallet.ts | 13 +++++---- 2 files changed, 39 insertions(+), 5 deletions(-) create mode 100644 packages/web3-test/src/test-wallet.test.ts diff --git a/packages/web3-test/src/test-wallet.test.ts b/packages/web3-test/src/test-wallet.test.ts new file mode 100644 index 000000000..354035c82 --- /dev/null +++ b/packages/web3-test/src/test-wallet.test.ts @@ -0,0 +1,31 @@ +/* +Copyright 2018 - 2022 The Alephium Authors +This file is part of the alephium project. + +The library is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +The library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with the library. If not, see . +*/ + +import { TOTAL_NUMBER_OF_GROUPS, groupOfAddress } from '@alephium/web3' +import { randomContractAddress } from './test-wallet' + +describe('test-wallet', function () { + it('should generate random contract id by group index', () => { + for (let group = 0; group < TOTAL_NUMBER_OF_GROUPS; group += 1) { + const contractAddress = randomContractAddress(group) + expect(groupOfAddress(contractAddress)).toEqual(group) + } + expect(() => randomContractAddress(TOTAL_NUMBER_OF_GROUPS)).toThrow('Invalid group index') + expect(() => randomContractAddress(-1)).toThrow('Invalid group index') + }) +}) diff --git a/packages/web3-test/src/test-wallet.ts b/packages/web3-test/src/test-wallet.ts index 5d1f3e89e..f104e8f9e 100644 --- a/packages/web3-test/src/test-wallet.ts +++ b/packages/web3-test/src/test-wallet.ts @@ -26,7 +26,8 @@ import { ALPH_TOKEN_ID, DUST_AMOUNT, Address, - TOTAL_NUMBER_OF_GROUPS + TOTAL_NUMBER_OF_GROUPS, + binToHex } from '@alephium/web3' import { NodeWallet, PrivateKeyWallet } from '@alephium/web3-wallet' import { randomBytes } from 'crypto' @@ -142,10 +143,12 @@ export async function expectAssertionError( ) } -export function randomContractId(): string { - return randomBytes(32).toString('hex') +export function randomContractId(groupIndex = 0): string { + checkGroup(groupIndex) + const bytes = new Uint8Array([...randomBytes(31), groupIndex]) + return binToHex(bytes) } -export function randomContractAddress(): string { - return addressFromContractId(randomContractId()) +export function randomContractAddress(groupIndex = 0): string { + return addressFromContractId(randomContractId(groupIndex)) } From 3caaa158d084dd840c152a6bb2aaff3c97a2dca4 Mon Sep 17 00:00:00 2001 From: lbqds Date: Thu, 4 Jul 2024 19:52:54 +0800 Subject: [PATCH 2/4] Fix decode address --- packages/web3/src/address/address.test.ts | 1 + packages/web3/src/codec/codec.ts | 13 +++++++++++++ packages/web3/src/codec/contract-codec.ts | 2 +- packages/web3/src/codec/lockup-script-codec.ts | 4 ++-- packages/web3/src/codec/unlock-script-codec.ts | 4 ++-- 5 files changed, 19 insertions(+), 5 deletions(-) diff --git a/packages/web3/src/address/address.test.ts b/packages/web3/src/address/address.test.ts index eb8a06170..fe84df068 100644 --- a/packages/web3/src/address/address.test.ts +++ b/packages/web3/src/address/address.test.ts @@ -56,6 +56,7 @@ describe('address', function () { expect(() => validateAddress('2jVWAcAPphJ8ueZNG1BPwbfPFjjbvorprceuqzgmJQ1ZRyELRpWgARvdB3T9trqpiJs7f4GkudPt6rQLnGbQYqq2NCi') ).toThrow('Invalid multisig address, n: 2, m: 3') + expect(() => validateAddress('thebear')).toThrow('Invalid multisig address') }) it('should get address type', () => { diff --git a/packages/web3/src/codec/codec.ts b/packages/web3/src/codec/codec.ts index 88d7cfbb8..60853badd 100644 --- a/packages/web3/src/codec/codec.ts +++ b/packages/web3/src/codec/codec.ts @@ -28,3 +28,16 @@ export function assert(value: boolean, message: string) { throw new Error(message) } } + +export function fixedSizeBytes(name: string, length: number): Parser { + return Parser.start().wrapped({ + length, + type: Parser.start().buffer(name, { length }), + wrapper: function (result) { + if (result.length === length) { + return result + } + throw new Error(`Too few bytes when parsing ${name}, expected ${length}, got ${result.length}`) + } + }) +} diff --git a/packages/web3/src/codec/contract-codec.ts b/packages/web3/src/codec/contract-codec.ts index 556732ac8..cce1d0b93 100644 --- a/packages/web3/src/codec/contract-codec.ts +++ b/packages/web3/src/codec/contract-codec.ts @@ -19,7 +19,7 @@ along with the library. If not, see . import { Parser } from 'binary-parser' import { ArrayCodec, DecodedArray } from './array-codec' import { Codec } from './codec' -import { compactSignedIntCodec, compactUnsignedIntCodec, DecodedCompactInt } from './compact-int-codec' +import { compactSignedIntCodec, DecodedCompactInt } from './compact-int-codec' import { Method, MethodCodec, methodCodec } from './method-codec' import { concatBytes } from '../utils' diff --git a/packages/web3/src/codec/lockup-script-codec.ts b/packages/web3/src/codec/lockup-script-codec.ts index 2adec2540..7455d4d81 100644 --- a/packages/web3/src/codec/lockup-script-codec.ts +++ b/packages/web3/src/codec/lockup-script-codec.ts @@ -17,7 +17,7 @@ along with the library. If not, see . */ import { Parser } from 'binary-parser' import { DecodedCompactInt, compactSignedIntCodec } from './compact-int-codec' -import { Codec } from './codec' +import { Codec, fixedSizeBytes } from './codec' import { ArrayCodec, DecodedArray } from './array-codec' export interface PublicKeyHash { @@ -25,7 +25,7 @@ export interface PublicKeyHash { } class PublicKeyHashCodec implements Codec { - parser = Parser.start().buffer('publicKeyHash', { length: 32 }) + parser = fixedSizeBytes('publicKeyHash', 32) encode(input: PublicKeyHash): Uint8Array { return input.publicKeyHash diff --git a/packages/web3/src/codec/unlock-script-codec.ts b/packages/web3/src/codec/unlock-script-codec.ts index 00f24da3a..7b9640e3d 100644 --- a/packages/web3/src/codec/unlock-script-codec.ts +++ b/packages/web3/src/codec/unlock-script-codec.ts @@ -18,7 +18,7 @@ along with the library. If not, see . import { Parser } from 'binary-parser' import { ArrayCodec, DecodedArray } from './array-codec' import { compactUnsignedIntCodec, compactSignedIntCodec, DecodedCompactInt } from './compact-int-codec' -import { Codec } from './codec' +import { Codec, fixedSizeBytes } from './codec' import { DecodedScript, scriptCodec } from './script-codec' import { ByteString, byteStringCodec } from './bytestring-codec' import { LockupScript, lockupScriptCodec } from './lockup-script-codec' @@ -29,7 +29,7 @@ export interface P2PKH { } class P2PKHCodec implements Codec { - parser = Parser.start().buffer('publicKey', { length: 33 }) + parser = fixedSizeBytes('publicKey', 33) encode(input: P2PKH): Uint8Array { return input.publicKey From 83c47fad88bb2220a02b169bebd80677fa55ec0f Mon Sep 17 00:00:00 2001 From: lbqds Date: Thu, 4 Jul 2024 20:44:17 +0800 Subject: [PATCH 3/4] Fix code generation for events with empty fields --- .project.json | 12 ++++++------ artifacts/add/Add.ral.json | 29 ++++++++--------------------- artifacts/add/AddMain.ral.json | 5 +---- artifacts/ts/Add.ts | 24 ++++++++++++++++++++---- contracts/add/add.ral | 2 ++ packages/cli/src/codegen.ts | 2 +- test/contract.test.ts | 16 +++++++++++----- 7 files changed, 49 insertions(+), 41 deletions(-) diff --git a/.project.json b/.project.json index 438256c7d..3f11108b6 100644 --- a/.project.json +++ b/.project.json @@ -11,9 +11,9 @@ "infos": { "Add": { "sourceFile": "add/add.ral", - "sourceCodeHash": "b3a4696a12e8cbb2cb5fb1ebe18000beb0e67e9ff7194300b01f9cff8862701c", - "bytecodeDebugPatch": "=12-2+5a=3-1+f=2-2+ac=2+b=1-1=83+77e010a=1+1646450726976617465=262", - "codeHashDebug": "34b2d26e23a53fafc6d898ca4911f50ebc782e3d2836af0f235f2e18c6875dd3", + "sourceCodeHash": "e53e74ff98c352525e856abd4311868176d45c61c957791c3d42eae274088df0", + "bytecodeDebugPatch": "=12-2+5c=2-2+81=3-1+e=2-2+bc=83-1+97e010a61646450726976617465=266", + "codeHashDebug": "c46db1ae7bae8b332c115869126eb1402bc71574925608a2adcea3cf7b9f8e7e", "warnings": [ "Found unused variables in Add: add2.addS, add2.address, add2.array2", "The return values of the function \"Add.copyCreateSubContract\" are not used. If this is intentional, consider using anonymous variables to suppress this warning.", @@ -26,7 +26,7 @@ }, "AddMain": { "sourceFile": "add/add.ral", - "sourceCodeHash": "b3a4696a12e8cbb2cb5fb1ebe18000beb0e67e9ff7194300b01f9cff8862701c", + "sourceCodeHash": "e53e74ff98c352525e856abd4311868176d45c61c957791c3d42eae274088df0", "bytecodeDebugPatch": "", "codeHashDebug": "", "warnings": [ @@ -35,14 +35,14 @@ }, "AddStruct1": { "sourceFile": "add/add.ral", - "sourceCodeHash": "b3a4696a12e8cbb2cb5fb1ebe18000beb0e67e9ff7194300b01f9cff8862701c", + "sourceCodeHash": "e53e74ff98c352525e856abd4311868176d45c61c957791c3d42eae274088df0", "bytecodeDebugPatch": "", "codeHashDebug": "", "warnings": [] }, "AddStruct2": { "sourceFile": "add/add.ral", - "sourceCodeHash": "b3a4696a12e8cbb2cb5fb1ebe18000beb0e67e9ff7194300b01f9cff8862701c", + "sourceCodeHash": "e53e74ff98c352525e856abd4311868176d45c61c957791c3d42eae274088df0", "bytecodeDebugPatch": "", "codeHashDebug": "", "warnings": [] diff --git a/artifacts/add/Add.ral.json b/artifacts/add/Add.ral.json index ddd49c08d..0ab9fc8b3 100644 --- a/artifacts/add/Add.ral.json +++ b/artifacts/add/Add.ral.json @@ -1,8 +1,8 @@ { - "version": "v2.14.7", + "version": "v3.2.0", "name": "Add", - "bytecode": "0206124024404d4072409f40ad010002100205d34fbb20db1600160100020201000c0c0205d36a51f82d1600160100020200000202021605160016015f06160016015fa00016002a16012aa100a000160016010e0dce000100020103040c0011d319adf50e1300641600130164170517041603d1a21601160216041605c118010104060015d3f6ce55a6130064160013016417051704160316021340c8ac1603d1a21601160216041605c118010201010003d320f98f621600b0", - "codeHash": "eb94a22e8e2600075ff538c6e79cb46e7a8d4fae5c29eee9f5afd7fabd26426b", + "bytecode": "0206124024404f407440a140af010002100205d34fbb20db1600160100020201000c0c0205d36a51f82d1600160100020200000202021805160016015f06160016015f075da00016002a16012aa100a000160016010e0dce000100020103040c0011d319adf50e1300641600130164170517041603d1a21601160216041605c118010104060015d3f6ce55a6130064160013016417051704160316021340c8ac1603d1a21601160216041605c118010201010003d320f98f621600b0", + "codeHash": "6a1f1415a68d55365e205b39e5418cd1f6fc8c2c7926d3662c77d69b55c92681", "fieldsSig": { "names": [ "sub", @@ -39,14 +39,16 @@ "U256", "U256" ] + }, + { + "name": "Empty", + "fieldNames": [], + "fieldTypes": [] } ], "functions": [ { "name": "add", - "usePreapprovedAssets": false, - "useAssetsInContract": false, - "isPublic": true, "paramNames": [ "array" ], @@ -62,9 +64,6 @@ }, { "name": "add2", - "usePreapprovedAssets": false, - "useAssetsInContract": false, - "isPublic": true, "paramNames": [ "array1", "address", @@ -89,9 +88,6 @@ }, { "name": "addPrivate", - "usePreapprovedAssets": false, - "useAssetsInContract": false, - "isPublic": false, "paramNames": [ "array" ], @@ -107,9 +103,6 @@ }, { "name": "createSubContract", - "usePreapprovedAssets": true, - "useAssetsInContract": false, - "isPublic": true, "paramNames": [ "a", "path", @@ -132,9 +125,6 @@ }, { "name": "createSubContractAndTransfer", - "usePreapprovedAssets": true, - "useAssetsInContract": true, - "isPublic": true, "paramNames": [ "a", "path", @@ -157,9 +147,6 @@ }, { "name": "destroy", - "usePreapprovedAssets": false, - "useAssetsInContract": true, - "isPublic": true, "paramNames": [ "caller" ], diff --git a/artifacts/add/AddMain.ral.json b/artifacts/add/AddMain.ral.json index 1a961f431..f890728a6 100644 --- a/artifacts/add/AddMain.ral.json +++ b/artifacts/add/AddMain.ral.json @@ -1,5 +1,5 @@ { - "version": "v2.14.7", + "version": "v3.2.0", "name": "AddMain", "bytecodeTemplate": "0101030002000c{1}{2}17011700160016010e0e{0}01001818", "fieldsSig": { @@ -19,9 +19,6 @@ "functions": [ { "name": "main", - "usePreapprovedAssets": true, - "useAssetsInContract": false, - "isPublic": true, "paramNames": [], "paramTypes": [], "paramIsMutable": [], diff --git a/artifacts/ts/Add.ts b/artifacts/ts/Add.ts index 6b95f36e8..e5014d2c6 100644 --- a/artifacts/ts/Add.ts +++ b/artifacts/ts/Add.ts @@ -53,6 +53,7 @@ export namespace AddTypes { export type AddEvent = ContractEvent<{ x: bigint; y: bigint }>; export type Add1Event = ContractEvent<{ a: bigint; b: bigint }>; + export type EmptyEvent = ContractEvent<{}>; export interface CallMethodTable { add: { @@ -160,7 +161,7 @@ class Factory extends ContractFactory { return this.contract.getInitialFieldsWithDefaultValues() as AddTypes.Fields; } - eventIndex = { Add: 0, Add1: 1 }; + eventIndex = { Add: 0, Add1: 1, Empty: 2 }; at(address: string): AddInstance { return new AddInstance(address); @@ -237,8 +238,8 @@ class Factory extends ContractFactory { export const Add = new Factory( Contract.fromJson( AddContractJson, - "=12-2+5a=3-1+f=2-2+ac=2+b=1-1=83+77e010a=1+1646450726976617465=262", - "34b2d26e23a53fafc6d898ca4911f50ebc782e3d2836af0f235f2e18c6875dd3", + "=12-2+5c=2-2+81=3-1+e=2-2+bc=83-1+97e010a61646450726976617465=266", + "c46db1ae7bae8b332c115869126eb1402bc71574925608a2adcea3cf7b9f8e7e", AllStructs ) ); @@ -283,8 +284,23 @@ export class AddInstance extends ContractInstance { ); } + subscribeEmptyEvent( + options: EventSubscribeOptions, + fromCount?: number + ): EventSubscription { + return subscribeContractEvent( + Add.contract, + this, + options, + "Empty", + fromCount + ); + } + subscribeAllEvents( - options: EventSubscribeOptions, + options: EventSubscribeOptions< + AddTypes.AddEvent | AddTypes.Add1Event | AddTypes.EmptyEvent + >, fromCount?: number ): EventSubscription { return subscribeContractEvents(Add.contract, this, options, fromCount); diff --git a/contracts/add/add.ral b/contracts/add/add.ral index a869d346c..06becc3a1 100644 --- a/contracts/add/add.ral +++ b/contracts/add/add.ral @@ -11,6 +11,7 @@ struct AddStruct2 { Contract Add(sub: Sub, mut result : U256) { event Add(x: U256, y: U256) event Add1(a: U256, b: U256) + event Empty() pub fn add(array: [U256; 2]) -> ([U256; 2]) { return addPrivate(array) @@ -25,6 +26,7 @@ Contract Add(sub: Sub, mut result : U256) { emit Debug(`addPrivate`) emit Add(array[0], array[1]) emit Add1(array[0], array[1]) + emit Empty() result = result + array[0] + array[1] return [result, sub.sub(array)] } diff --git a/packages/cli/src/codegen.ts b/packages/cli/src/codegen.ts index 134c3f20b..d62366b57 100644 --- a/packages/cli/src/codegen.ts +++ b/packages/cli/src/codegen.ts @@ -197,7 +197,7 @@ function getEventType(event: EventSig): string { function genEventType(event: EventSig): string { if (event.fieldNames.length === 0) { - return `export type ${getEventType(event)} = Omit` + return `export type ${getEventType(event)} = ContractEvent<{}>` } const fieldsType = `{${formatParameters({ names: event.fieldNames, types: event.fieldTypes })}}` return `export type ${getEventType(event)} = ContractEvent<${fieldsType}>` diff --git a/test/contract.test.ts b/test/contract.test.ts index 35b01d617..e47d060c2 100644 --- a/test/contract.test.ts +++ b/test/contract.test.ts @@ -82,6 +82,7 @@ describe('contract', function () { it('should test event index', () => { expect(Add.eventIndex.Add).toEqual(0) expect(Add.eventIndex.Add1).toEqual(1) + expect(Add.eventIndex.Empty).toEqual(2) expect(Sub.eventIndex.Sub).toEqual(0) }) @@ -127,11 +128,16 @@ describe('contract', function () { expect(event1.fields.a).toEqual(2n) expect(event1.fields.b).toEqual(1n) - const event2 = events[2] as SubTypes.SubEvent - expect(event2.name).toEqual('Sub') - expect(event2.eventIndex).toEqual(0) - expect(event2.fields.x).toEqual(2n) - expect(event2.fields.y).toEqual(1n) + const event2 = events[2] as AddTypes.EmptyEvent + expect(event2.name).toEqual('Empty') + expect(event2.eventIndex).toEqual(2) + expect(event2.fields).toEqual({}) + + const event3 = events[3] as SubTypes.SubEvent + expect(event3.name).toEqual('Sub') + expect(event3.eventIndex).toEqual(0) + expect(event3.fields.x).toEqual(2n) + expect(event3.fields.y).toEqual(1n) } checkEvents(testResult.events) From 6dc9ad14b8dcae23f786140bc5be89479c9e6e62 Mon Sep 17 00:00:00 2001 From: lbqds Date: Thu, 4 Jul 2024 21:11:21 +0800 Subject: [PATCH 4/4] Fix event.test.ts --- test/events.test.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/test/events.test.ts b/test/events.test.ts index 83e44e154..dcd2e0f05 100644 --- a/test/events.test.ts +++ b/test/events.test.ts @@ -93,7 +93,7 @@ describe('events', function () { it('should subscribe all events', async () => { const add = await deployContract(signer) - type EventTypes = AddTypes.AddEvent | AddTypes.Add1Event + type EventTypes = AddTypes.AddEvent | AddTypes.Add1Event | AddTypes.EmptyEvent const addEvents: Array = [] const subscribeOptions = createSubscribeOptions(addEvents) const subscription = add.subscribeAllEvents(subscribeOptions) @@ -110,7 +110,11 @@ describe('events', function () { return (event).fields.a !== undefined } - expect(addEvents.length).toEqual(6) + const isEmpty = (event: EventTypes): event is AddTypes.EmptyEvent => { + return Object.keys((event).fields).length === 0 + } + + expect(addEvents.length).toEqual(9) addEvents.forEach((event) => { if (isAdd(event)) { expect(event.fields.x).toEqual(2n) @@ -122,6 +126,10 @@ describe('events', function () { expect(event.fields.b).toEqual(1n) expect(event.name).toEqual('Add1') expect(event.eventIndex).toEqual(1) + } else if (isEmpty(event)) { + expect(event.fields).toEqual({}) + expect(event.name).toEqual('Empty') + expect(event.eventIndex).toEqual(2) } else { expect(false).toEqual(true) }