@btc-vision/transaction 1.3.5 → 1.3.6
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.
- package/browser/_version.d.ts +1 -1
- package/browser/generators/Features.d.ts +9 -4
- package/browser/generators/Generator.d.ts +5 -2
- package/browser/generators/builders/CalldataGenerator.d.ts +2 -2
- package/browser/generators/builders/LegacyCalldataGenerator.d.ts +2 -2
- package/browser/index.js +1 -1
- package/browser/transaction/builders/InteractionTransaction.d.ts +1 -0
- package/browser/transaction/interfaces/ITransactionParameters.d.ts +4 -0
- package/build/_version.d.ts +1 -1
- package/build/_version.js +1 -1
- package/build/generators/Features.d.ts +9 -4
- package/build/generators/Features.js +1 -5
- package/build/generators/Generator.d.ts +5 -2
- package/build/generators/Generator.js +40 -5
- package/build/generators/builders/CalldataGenerator.d.ts +2 -2
- package/build/generators/builders/CalldataGenerator.js +10 -4
- package/build/generators/builders/DeploymentGenerator.js +13 -2
- package/build/generators/builders/LegacyCalldataGenerator.d.ts +2 -2
- package/build/generators/builders/LegacyCalldataGenerator.js +10 -4
- package/build/transaction/builders/InteractionTransaction.d.ts +1 -0
- package/build/transaction/builders/InteractionTransaction.js +12 -1
- package/build/transaction/interfaces/ITransactionParameters.d.ts +4 -0
- package/eslint.config.js +1 -0
- package/package.json +1 -1
- package/src/_version.ts +1 -1
- package/src/generators/Features.ts +10 -5
- package/src/generators/Generator.ts +54 -5
- package/src/generators/builders/CalldataGenerator.ts +16 -9
- package/src/generators/builders/DeploymentGenerator.ts +17 -1
- package/src/generators/builders/LegacyCalldataGenerator.ts +14 -6
- package/src/transaction/builders/InteractionTransaction.ts +15 -0
- package/src/transaction/interfaces/ITransactionParameters.ts +7 -1
|
@@ -2,6 +2,9 @@ import { UTXO } from '../../utxo/interfaces/IUTXO.js';
|
|
|
2
2
|
import { ITweakedTransactionData } from '../shared/TweakedTransaction.js';
|
|
3
3
|
import { ChainId } from '../../network/ChainId.js';
|
|
4
4
|
import { PsbtOutputExtended } from '@btc-vision/bitcoin';
|
|
5
|
+
export interface LoadedStorage {
|
|
6
|
+
[key: string]: string[];
|
|
7
|
+
}
|
|
5
8
|
export interface ITransactionParameters extends ITweakedTransactionData {
|
|
6
9
|
readonly from?: string;
|
|
7
10
|
readonly to?: string;
|
|
@@ -28,6 +31,7 @@ export interface SharedInteractionParameters extends ITransactionParameters {
|
|
|
28
31
|
disableAutoRefund?: boolean;
|
|
29
32
|
readonly preimage: Buffer;
|
|
30
33
|
readonly randomBytes?: Buffer;
|
|
34
|
+
readonly loadedStorage?: LoadedStorage;
|
|
31
35
|
}
|
|
32
36
|
export interface IInteractionParameters extends SharedInteractionParameters {
|
|
33
37
|
readonly calldata: Buffer;
|
package/build/_version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const version = "1.3.
|
|
1
|
+
export declare const version = "1.3.6";
|
package/build/_version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = '1.3.
|
|
1
|
+
export const version = '1.3.6';
|
|
@@ -1,6 +1,11 @@
|
|
|
1
|
+
import { LoadedStorage } from '../transaction/interfaces/ITransactionParameters.js';
|
|
1
2
|
export declare enum Features {
|
|
2
|
-
|
|
3
|
+
ACCESS_LIST = 1
|
|
4
|
+
}
|
|
5
|
+
export interface Feature<T extends Features> {
|
|
6
|
+
opcode: T;
|
|
7
|
+
data: unknown;
|
|
8
|
+
}
|
|
9
|
+
export interface AccessListFeature extends Feature<Features.ACCESS_LIST> {
|
|
10
|
+
data: LoadedStorage;
|
|
3
11
|
}
|
|
4
|
-
export declare const FeatureOpCodes: {
|
|
5
|
-
[key: number]: number;
|
|
6
|
-
};
|
|
@@ -1,8 +1,4 @@
|
|
|
1
|
-
import { opcodes } from '@btc-vision/bitcoin';
|
|
2
1
|
export var Features;
|
|
3
2
|
(function (Features) {
|
|
4
|
-
Features[Features["
|
|
3
|
+
Features[Features["ACCESS_LIST"] = 1] = "ACCESS_LIST";
|
|
5
4
|
})(Features || (Features = {}));
|
|
6
|
-
export const FeatureOpCodes = {
|
|
7
|
-
[Features.UNWRAP]: opcodes.OP_16,
|
|
8
|
-
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Network } from '@btc-vision/bitcoin';
|
|
2
|
+
import { Feature, Features } from './Features.js';
|
|
2
3
|
export declare abstract class Generator {
|
|
3
4
|
static readonly DATA_CHUNK_SIZE: number;
|
|
4
5
|
static readonly MAGIC: Buffer;
|
|
@@ -7,8 +8,10 @@ export declare abstract class Generator {
|
|
|
7
8
|
protected readonly contractSaltPubKey?: Buffer;
|
|
8
9
|
protected readonly network: Network;
|
|
9
10
|
protected constructor(senderPubKey: Buffer, contractSaltPubKey?: Buffer, network?: Network);
|
|
10
|
-
|
|
11
|
-
getHeader(maxPriority: bigint): Buffer;
|
|
11
|
+
buildHeader(features: Features[]): Buffer;
|
|
12
|
+
getHeader(maxPriority: bigint, features?: Features[]): Buffer;
|
|
12
13
|
abstract compile(...args: unknown[]): Buffer;
|
|
13
14
|
protected splitBufferIntoChunks(buffer: Buffer, chunkSize?: number): Array<Buffer[]>;
|
|
15
|
+
protected encodeFeature(feature: Feature<Features>): Buffer[][];
|
|
16
|
+
private encodeAccessListFeature;
|
|
14
17
|
}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { networks, toXOnly } from '@btc-vision/bitcoin';
|
|
2
2
|
import { BinaryWriter } from '../buffer/BinaryWriter.js';
|
|
3
|
+
import { Features } from './Features.js';
|
|
4
|
+
import { Address } from '../keypair/Address.js';
|
|
5
|
+
import { Compressor } from '../bytecode/Compressor.js';
|
|
3
6
|
export class Generator {
|
|
4
7
|
constructor(senderPubKey, contractSaltPubKey, network = networks.bitcoin) {
|
|
5
8
|
this.network = networks.bitcoin;
|
|
@@ -8,12 +11,18 @@ export class Generator {
|
|
|
8
11
|
this.network = network;
|
|
9
12
|
this.xSenderPubKey = toXOnly(senderPubKey);
|
|
10
13
|
}
|
|
11
|
-
|
|
12
|
-
|
|
14
|
+
buildHeader(features) {
|
|
15
|
+
let flags = 0;
|
|
16
|
+
for (const feature of features) {
|
|
17
|
+
flags |= feature;
|
|
18
|
+
}
|
|
19
|
+
const bytesU24 = Buffer.alloc(3);
|
|
20
|
+
bytesU24.writeUIntBE(flags, 0, 3);
|
|
21
|
+
return Buffer.from([this.senderPubKey[0], ...bytesU24]);
|
|
13
22
|
}
|
|
14
|
-
getHeader(maxPriority) {
|
|
15
|
-
const writer = new BinaryWriter(
|
|
16
|
-
writer.writeBytes(this.
|
|
23
|
+
getHeader(maxPriority, features = []) {
|
|
24
|
+
const writer = new BinaryWriter(12);
|
|
25
|
+
writer.writeBytes(this.buildHeader(features));
|
|
17
26
|
writer.writeU64(maxPriority);
|
|
18
27
|
return Buffer.from(writer.getBuffer());
|
|
19
28
|
}
|
|
@@ -29,6 +38,32 @@ export class Generator {
|
|
|
29
38
|
}
|
|
30
39
|
return chunks;
|
|
31
40
|
}
|
|
41
|
+
encodeFeature(feature) {
|
|
42
|
+
switch (feature.opcode) {
|
|
43
|
+
case Features.ACCESS_LIST:
|
|
44
|
+
return this.splitBufferIntoChunks(this.encodeAccessListFeature(feature));
|
|
45
|
+
default:
|
|
46
|
+
throw new Error(`Unknown feature type: ${feature.opcode}`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
encodeAccessListFeature(feature) {
|
|
50
|
+
const writer = new BinaryWriter();
|
|
51
|
+
writer.writeU16(Object.keys(feature.data).length);
|
|
52
|
+
for (const contract in feature.data) {
|
|
53
|
+
const parsedContract = Address.fromString(contract);
|
|
54
|
+
const data = feature.data[contract];
|
|
55
|
+
writer.writeAddress(parsedContract);
|
|
56
|
+
writer.writeU32(data.length);
|
|
57
|
+
for (const pointer of data) {
|
|
58
|
+
const pointerBuffer = Buffer.from(pointer, 'base64');
|
|
59
|
+
if (pointerBuffer.length !== 32) {
|
|
60
|
+
throw new Error(`Invalid pointer length: ${pointerBuffer.length}`);
|
|
61
|
+
}
|
|
62
|
+
writer.writeBytes(pointerBuffer);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return Compressor.compress(Buffer.from(writer.getBuffer()));
|
|
66
|
+
}
|
|
32
67
|
}
|
|
33
68
|
Generator.DATA_CHUNK_SIZE = 512;
|
|
34
69
|
Generator.MAGIC = Buffer.from('op', 'utf-8');
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Network } from '@btc-vision/bitcoin';
|
|
2
|
-
import { Features } from '../Features.js';
|
|
2
|
+
import { Feature, Features } from '../Features.js';
|
|
3
3
|
import { Generator } from '../Generator.js';
|
|
4
4
|
export declare class CalldataGenerator extends Generator {
|
|
5
5
|
constructor(senderPubKey: Buffer, contractSaltPubKey: Buffer, network?: Network);
|
|
6
6
|
static getPubKeyAsBuffer(witnessKeys: Buffer[], network: Network): Buffer;
|
|
7
|
-
compile(calldata: Buffer, contractSecret: Buffer, preimage: Buffer, maxPriority: bigint, features?: Features[]): Buffer;
|
|
7
|
+
compile(calldata: Buffer, contractSecret: Buffer, preimage: Buffer, maxPriority: bigint, features?: Feature<Features>[]): Buffer;
|
|
8
8
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { crypto, networks, opcodes, script } from '@btc-vision/bitcoin';
|
|
2
2
|
import { Compressor } from '../../bytecode/Compressor.js';
|
|
3
3
|
import { EcKeyPair } from '../../keypair/EcKeyPair.js';
|
|
4
|
-
import { FeatureOpCodes } from '../Features.js';
|
|
5
4
|
import { Generator } from '../Generator.js';
|
|
6
5
|
export class CalldataGenerator extends Generator {
|
|
7
6
|
constructor(senderPubKey, contractSaltPubKey, network = networks.bitcoin) {
|
|
@@ -31,8 +30,16 @@ export class CalldataGenerator extends Generator {
|
|
|
31
30
|
const dataChunks = this.splitBufferIntoChunks(calldata);
|
|
32
31
|
if (!dataChunks.length)
|
|
33
32
|
throw new Error('No data chunks found');
|
|
33
|
+
const featuresList = [];
|
|
34
|
+
const featureData = [];
|
|
35
|
+
for (let i = 0; i < features.length; i++) {
|
|
36
|
+
const feature = features[i];
|
|
37
|
+
featuresList.push(feature.opcode);
|
|
38
|
+
const data = this.encodeFeature(feature);
|
|
39
|
+
featureData.push(...data);
|
|
40
|
+
}
|
|
34
41
|
let compiledData = [
|
|
35
|
-
this.getHeader(maxPriority),
|
|
42
|
+
this.getHeader(maxPriority, featuresList),
|
|
36
43
|
opcodes.OP_TOALTSTACK,
|
|
37
44
|
preimage,
|
|
38
45
|
opcodes.OP_TOALTSTACK,
|
|
@@ -53,8 +60,7 @@ export class CalldataGenerator extends Generator {
|
|
|
53
60
|
opcodes.OP_IF,
|
|
54
61
|
Generator.MAGIC,
|
|
55
62
|
];
|
|
56
|
-
|
|
57
|
-
compiledData = compiledData.concat(...featureOpcodes, ...[opcodes.OP_1NEGATE, ...dataChunks, opcodes.OP_ELSE, opcodes.OP_1, opcodes.OP_ENDIF]);
|
|
63
|
+
compiledData = compiledData.concat(...featureData, ...[opcodes.OP_1NEGATE, ...dataChunks, opcodes.OP_ELSE, opcodes.OP_1, opcodes.OP_ENDIF]);
|
|
58
64
|
const asm = compiledData.flat();
|
|
59
65
|
const compiled = script.compile(asm);
|
|
60
66
|
const decompiled = script.decompile(compiled);
|
|
@@ -13,13 +13,23 @@ export class DeploymentGenerator extends Generator {
|
|
|
13
13
|
}
|
|
14
14
|
return compiled;
|
|
15
15
|
}
|
|
16
|
-
getAsm(contractBytecode, contractSalt, preimage, maxPriority, calldata) {
|
|
16
|
+
getAsm(contractBytecode, contractSalt, preimage, maxPriority, calldata, features) {
|
|
17
17
|
if (!this.contractSaltPubKey)
|
|
18
18
|
throw new Error('Contract salt public key not set');
|
|
19
19
|
const dataChunks = this.splitBufferIntoChunks(contractBytecode);
|
|
20
20
|
const calldataChunks = calldata ? this.splitBufferIntoChunks(calldata) : [];
|
|
21
|
+
const featuresList = [];
|
|
22
|
+
const featureData = [];
|
|
23
|
+
if (features) {
|
|
24
|
+
for (let i = 0; i < features.length; i++) {
|
|
25
|
+
const feature = features[i];
|
|
26
|
+
featuresList.push(feature.opcode);
|
|
27
|
+
const data = this.encodeFeature(feature);
|
|
28
|
+
featureData.push(...data);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
21
31
|
const compiledData = [
|
|
22
|
-
this.getHeader(maxPriority),
|
|
32
|
+
this.getHeader(maxPriority, featuresList),
|
|
23
33
|
opcodes.OP_TOALTSTACK,
|
|
24
34
|
preimage,
|
|
25
35
|
opcodes.OP_TOALTSTACK,
|
|
@@ -39,6 +49,7 @@ export class DeploymentGenerator extends Generator {
|
|
|
39
49
|
opcodes.OP_NUMEQUAL,
|
|
40
50
|
opcodes.OP_IF,
|
|
41
51
|
Generator.MAGIC,
|
|
52
|
+
...featureData,
|
|
42
53
|
opcodes.OP_0,
|
|
43
54
|
...calldataChunks,
|
|
44
55
|
opcodes.OP_1NEGATE,
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Network } from '@btc-vision/bitcoin';
|
|
2
|
-
import { Features } from '../Features.js';
|
|
3
2
|
import { Generator } from '../Generator.js';
|
|
3
|
+
import { Feature, Features } from '../Features.js';
|
|
4
4
|
export declare class LegacyCalldataGenerator extends Generator {
|
|
5
5
|
constructor(senderPubKey: Buffer, network?: Network);
|
|
6
6
|
static getPubKeyAsBuffer(witnessKeys: Buffer[], network: Network): Buffer;
|
|
7
|
-
compile(calldata: Buffer, contractSecret: Buffer, preimage: Buffer, maxPriority: bigint, features?: Features[]): Buffer;
|
|
7
|
+
compile(calldata: Buffer, contractSecret: Buffer, preimage: Buffer, maxPriority: bigint, features?: Feature<Features>[]): Buffer;
|
|
8
8
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { crypto, networks, opcodes, script } from '@btc-vision/bitcoin';
|
|
2
2
|
import { Compressor } from '../../bytecode/Compressor.js';
|
|
3
3
|
import { EcKeyPair } from '../../keypair/EcKeyPair.js';
|
|
4
|
-
import { FeatureOpCodes } from '../Features.js';
|
|
5
4
|
import { Generator } from '../Generator.js';
|
|
6
5
|
export class LegacyCalldataGenerator extends Generator {
|
|
7
6
|
constructor(senderPubKey, network = networks.bitcoin) {
|
|
@@ -29,8 +28,16 @@ export class LegacyCalldataGenerator extends Generator {
|
|
|
29
28
|
const dataChunks = this.splitBufferIntoChunks(calldata);
|
|
30
29
|
if (!dataChunks.length)
|
|
31
30
|
throw new Error('No data chunks found');
|
|
31
|
+
const featuresList = [];
|
|
32
|
+
const featureData = [];
|
|
33
|
+
for (let i = 0; i < features.length; i++) {
|
|
34
|
+
const feature = features[i];
|
|
35
|
+
featuresList.push(feature.opcode);
|
|
36
|
+
const data = this.encodeFeature(feature);
|
|
37
|
+
featureData.push(...data);
|
|
38
|
+
}
|
|
32
39
|
let compiledData = [
|
|
33
|
-
this.getHeader(maxPriority),
|
|
40
|
+
this.getHeader(maxPriority, featuresList),
|
|
34
41
|
opcodes.OP_TOALTSTACK,
|
|
35
42
|
preimage,
|
|
36
43
|
opcodes.OP_TOALTSTACK,
|
|
@@ -48,8 +55,7 @@ export class LegacyCalldataGenerator extends Generator {
|
|
|
48
55
|
opcodes.OP_IF,
|
|
49
56
|
Generator.MAGIC,
|
|
50
57
|
];
|
|
51
|
-
|
|
52
|
-
compiledData = compiledData.concat(...featureOpcodes, ...[opcodes.OP_1NEGATE, ...dataChunks, opcodes.OP_ELSE, opcodes.OP_1, opcodes.OP_ENDIF]);
|
|
58
|
+
compiledData = compiledData.concat(...featureData, ...[opcodes.OP_1NEGATE, ...dataChunks, opcodes.OP_ELSE, opcodes.OP_1, opcodes.OP_ENDIF]);
|
|
53
59
|
const asm = compiledData.flat();
|
|
54
60
|
const compiled = script.compile(asm);
|
|
55
61
|
const decompiled = script.decompile(compiled);
|
|
@@ -1,13 +1,24 @@
|
|
|
1
1
|
import { TransactionType } from '../enums/TransactionType.js';
|
|
2
2
|
import { SharedInteractionTransaction } from './SharedInteractionTransaction.js';
|
|
3
|
+
import { Features } from '../../generators/Features.js';
|
|
3
4
|
export class InteractionTransaction extends SharedInteractionTransaction {
|
|
4
5
|
constructor(parameters) {
|
|
5
6
|
super(parameters);
|
|
6
7
|
this.type = TransactionType.INTERACTION;
|
|
7
8
|
this.tapLeafScript = null;
|
|
8
9
|
this.contractSecret = this.generateSecret();
|
|
9
|
-
this.compiledTargetScript = this.calldataGenerator.compile(this.calldata, this.contractSecret, this.preimage, this.priorityFee);
|
|
10
|
+
this.compiledTargetScript = this.calldataGenerator.compile(this.calldata, this.contractSecret, this.preimage, this.priorityFee, this.generateFeatures(parameters));
|
|
10
11
|
this.scriptTree = this.getScriptTree();
|
|
11
12
|
this.internalInit();
|
|
12
13
|
}
|
|
14
|
+
generateFeatures(parameters) {
|
|
15
|
+
const features = [];
|
|
16
|
+
if (parameters.loadedStorage) {
|
|
17
|
+
features.push({
|
|
18
|
+
opcode: Features.ACCESS_LIST,
|
|
19
|
+
data: parameters.loadedStorage,
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
return features;
|
|
23
|
+
}
|
|
13
24
|
}
|
|
@@ -2,6 +2,9 @@ import { UTXO } from '../../utxo/interfaces/IUTXO.js';
|
|
|
2
2
|
import { ITweakedTransactionData } from '../shared/TweakedTransaction.js';
|
|
3
3
|
import { ChainId } from '../../network/ChainId.js';
|
|
4
4
|
import { PsbtOutputExtended } from '@btc-vision/bitcoin';
|
|
5
|
+
export interface LoadedStorage {
|
|
6
|
+
[key: string]: string[];
|
|
7
|
+
}
|
|
5
8
|
export interface ITransactionParameters extends ITweakedTransactionData {
|
|
6
9
|
readonly from?: string;
|
|
7
10
|
readonly to?: string;
|
|
@@ -28,6 +31,7 @@ export interface SharedInteractionParameters extends ITransactionParameters {
|
|
|
28
31
|
disableAutoRefund?: boolean;
|
|
29
32
|
readonly preimage: Buffer;
|
|
30
33
|
readonly randomBytes?: Buffer;
|
|
34
|
+
readonly loadedStorage?: LoadedStorage;
|
|
31
35
|
}
|
|
32
36
|
export interface IInteractionParameters extends SharedInteractionParameters {
|
|
33
37
|
readonly calldata: Buffer;
|
package/eslint.config.js
CHANGED
package/package.json
CHANGED
package/src/_version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = '1.3.
|
|
1
|
+
export const version = '1.3.6';
|
|
@@ -1,9 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { LoadedStorage } from '../transaction/interfaces/ITransactionParameters.js';
|
|
2
2
|
|
|
3
3
|
export enum Features {
|
|
4
|
-
|
|
4
|
+
ACCESS_LIST = 1,
|
|
5
5
|
}
|
|
6
6
|
|
|
7
|
-
export
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
export interface Feature<T extends Features> {
|
|
8
|
+
opcode: T;
|
|
9
|
+
data: unknown;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface AccessListFeature extends Feature<Features.ACCESS_LIST> {
|
|
13
|
+
data: LoadedStorage;
|
|
14
|
+
}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { Network, networks, toXOnly } from '@btc-vision/bitcoin';
|
|
2
2
|
import { BinaryWriter } from '../buffer/BinaryWriter.js';
|
|
3
|
+
import { AccessListFeature, Feature, Features } from './Features.js';
|
|
4
|
+
import { Address } from '../keypair/Address.js';
|
|
5
|
+
import { Compressor } from '../bytecode/Compressor.js';
|
|
3
6
|
|
|
4
7
|
/** Bitcoin Script Generator */
|
|
5
8
|
export abstract class Generator {
|
|
@@ -48,13 +51,22 @@ export abstract class Generator {
|
|
|
48
51
|
this.xSenderPubKey = toXOnly(senderPubKey);
|
|
49
52
|
}
|
|
50
53
|
|
|
51
|
-
public
|
|
52
|
-
|
|
54
|
+
public buildHeader(features: Features[]): Buffer {
|
|
55
|
+
let flags: number = 0;
|
|
56
|
+
|
|
57
|
+
for (const feature of features) {
|
|
58
|
+
flags |= feature;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const bytesU24 = Buffer.alloc(3);
|
|
62
|
+
bytesU24.writeUIntBE(flags, 0, 3);
|
|
63
|
+
|
|
64
|
+
return Buffer.from([this.senderPubKey[0], ...bytesU24]);
|
|
53
65
|
}
|
|
54
66
|
|
|
55
|
-
public getHeader(maxPriority: bigint): Buffer {
|
|
56
|
-
const writer = new BinaryWriter(
|
|
57
|
-
writer.writeBytes(this.
|
|
67
|
+
public getHeader(maxPriority: bigint, features: Features[] = []): Buffer {
|
|
68
|
+
const writer = new BinaryWriter(12);
|
|
69
|
+
writer.writeBytes(this.buildHeader(features));
|
|
58
70
|
writer.writeU64(maxPriority);
|
|
59
71
|
|
|
60
72
|
return Buffer.from(writer.getBuffer());
|
|
@@ -92,4 +104,41 @@ export abstract class Generator {
|
|
|
92
104
|
|
|
93
105
|
return chunks;
|
|
94
106
|
}
|
|
107
|
+
|
|
108
|
+
protected encodeFeature(feature: Feature<Features>): Buffer[][] {
|
|
109
|
+
switch (feature.opcode) {
|
|
110
|
+
case Features.ACCESS_LIST:
|
|
111
|
+
return this.splitBufferIntoChunks(
|
|
112
|
+
this.encodeAccessListFeature(feature as AccessListFeature),
|
|
113
|
+
);
|
|
114
|
+
default:
|
|
115
|
+
throw new Error(`Unknown feature type: ${feature.opcode}`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
private encodeAccessListFeature(feature: AccessListFeature): Buffer {
|
|
120
|
+
const writer = new BinaryWriter();
|
|
121
|
+
|
|
122
|
+
writer.writeU16(Object.keys(feature.data).length);
|
|
123
|
+
|
|
124
|
+
for (const contract in feature.data) {
|
|
125
|
+
const parsedContract = Address.fromString(contract);
|
|
126
|
+
const data = feature.data[contract];
|
|
127
|
+
|
|
128
|
+
writer.writeAddress(parsedContract);
|
|
129
|
+
writer.writeU32(data.length);
|
|
130
|
+
|
|
131
|
+
for (const pointer of data) {
|
|
132
|
+
const pointerBuffer = Buffer.from(pointer, 'base64');
|
|
133
|
+
|
|
134
|
+
if (pointerBuffer.length !== 32) {
|
|
135
|
+
throw new Error(`Invalid pointer length: ${pointerBuffer.length}`);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
writer.writeBytes(pointerBuffer);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return Compressor.compress(Buffer.from(writer.getBuffer()));
|
|
143
|
+
}
|
|
95
144
|
}
|
|
@@ -2,9 +2,8 @@ import { crypto, Network, networks, opcodes, script } from '@btc-vision/bitcoin'
|
|
|
2
2
|
import { ECPairInterface } from 'ecpair';
|
|
3
3
|
import { Compressor } from '../../bytecode/Compressor.js';
|
|
4
4
|
import { EcKeyPair } from '../../keypair/EcKeyPair.js';
|
|
5
|
-
import {
|
|
5
|
+
import { Feature, Features } from '../Features.js';
|
|
6
6
|
import { Generator } from '../Generator.js';
|
|
7
|
-
import { BinaryWriter } from '../../buffer/BinaryWriter.js';
|
|
8
7
|
|
|
9
8
|
/**
|
|
10
9
|
* Class to generate bitcoin script for interaction transactions
|
|
@@ -59,7 +58,7 @@ export class CalldataGenerator extends Generator {
|
|
|
59
58
|
* @param {Buffer} contractSecret - The contract secret
|
|
60
59
|
* @param preimage
|
|
61
60
|
* @param maxPriority - Amount of satoshis to spend max on priority fee
|
|
62
|
-
* @param {
|
|
61
|
+
* @param {Feature<Features>[]} features - The features to use
|
|
63
62
|
* @returns {Buffer} - The compiled script
|
|
64
63
|
* @throws {Error} - If something goes wrong
|
|
65
64
|
*/
|
|
@@ -68,15 +67,25 @@ export class CalldataGenerator extends Generator {
|
|
|
68
67
|
contractSecret: Buffer,
|
|
69
68
|
preimage: Buffer,
|
|
70
69
|
maxPriority: bigint,
|
|
71
|
-
features: Features[] = [],
|
|
70
|
+
features: Feature<Features>[] = [],
|
|
72
71
|
): Buffer {
|
|
73
72
|
if (!this.contractSaltPubKey) throw new Error('Contract salt public key not set');
|
|
74
73
|
|
|
75
74
|
const dataChunks: Buffer[][] = this.splitBufferIntoChunks(calldata);
|
|
76
75
|
if (!dataChunks.length) throw new Error('No data chunks found');
|
|
77
76
|
|
|
78
|
-
|
|
79
|
-
|
|
77
|
+
const featuresList: Features[] = [];
|
|
78
|
+
const featureData: (number | Buffer | Buffer[])[] = [];
|
|
79
|
+
for (let i = 0; i < features.length; i++) {
|
|
80
|
+
const feature = features[i];
|
|
81
|
+
featuresList.push(feature.opcode);
|
|
82
|
+
|
|
83
|
+
const data = this.encodeFeature(feature);
|
|
84
|
+
featureData.push(...data);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
let compiledData: (number | Buffer | Buffer[])[] = [
|
|
88
|
+
this.getHeader(maxPriority, featuresList),
|
|
80
89
|
opcodes.OP_TOALTSTACK,
|
|
81
90
|
|
|
82
91
|
// CHALLENGE PREIMAGE FOR REWARD,
|
|
@@ -105,11 +114,9 @@ export class CalldataGenerator extends Generator {
|
|
|
105
114
|
Generator.MAGIC,
|
|
106
115
|
];
|
|
107
116
|
|
|
108
|
-
const featureOpcodes = features.map((feature) => FeatureOpCodes[feature]); // Get the opcodes for the features
|
|
109
|
-
|
|
110
117
|
// Write calldata
|
|
111
118
|
compiledData = compiledData.concat(
|
|
112
|
-
...
|
|
119
|
+
...featureData,
|
|
113
120
|
...[opcodes.OP_1NEGATE, ...dataChunks, opcodes.OP_ELSE, opcodes.OP_1, opcodes.OP_ENDIF],
|
|
114
121
|
);
|
|
115
122
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { crypto, Network, networks, opcodes, script } from '@btc-vision/bitcoin';
|
|
2
2
|
import { Generator } from '../Generator.js';
|
|
3
|
+
import { Feature, Features } from '../Features.js';
|
|
3
4
|
|
|
4
5
|
export class DeploymentGenerator extends Generator {
|
|
5
6
|
constructor(
|
|
@@ -46,14 +47,28 @@ export class DeploymentGenerator extends Generator {
|
|
|
46
47
|
preimage: Buffer,
|
|
47
48
|
maxPriority: bigint,
|
|
48
49
|
calldata?: Buffer,
|
|
50
|
+
features?: Feature<Features>[],
|
|
49
51
|
): (number | Buffer)[] {
|
|
50
52
|
if (!this.contractSaltPubKey) throw new Error('Contract salt public key not set');
|
|
51
53
|
|
|
52
54
|
const dataChunks: Buffer[][] = this.splitBufferIntoChunks(contractBytecode);
|
|
53
55
|
const calldataChunks: Buffer[][] = calldata ? this.splitBufferIntoChunks(calldata) : [];
|
|
54
56
|
|
|
57
|
+
const featuresList: Features[] = [];
|
|
58
|
+
const featureData: (number | Buffer | Buffer[])[] = [];
|
|
59
|
+
|
|
60
|
+
if (features) {
|
|
61
|
+
for (let i = 0; i < features.length; i++) {
|
|
62
|
+
const feature = features[i];
|
|
63
|
+
featuresList.push(feature.opcode);
|
|
64
|
+
|
|
65
|
+
const data = this.encodeFeature(feature);
|
|
66
|
+
featureData.push(...data);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
55
70
|
const compiledData = [
|
|
56
|
-
this.getHeader(maxPriority),
|
|
71
|
+
this.getHeader(maxPriority, featuresList),
|
|
57
72
|
opcodes.OP_TOALTSTACK,
|
|
58
73
|
|
|
59
74
|
// CHALLENGE PREIMAGE FOR REWARD,
|
|
@@ -80,6 +95,7 @@ export class DeploymentGenerator extends Generator {
|
|
|
80
95
|
opcodes.OP_IF,
|
|
81
96
|
|
|
82
97
|
Generator.MAGIC,
|
|
98
|
+
...featureData,
|
|
83
99
|
opcodes.OP_0,
|
|
84
100
|
...calldataChunks,
|
|
85
101
|
opcodes.OP_1NEGATE,
|
|
@@ -2,8 +2,8 @@ import { crypto, Network, networks, opcodes, script } from '@btc-vision/bitcoin'
|
|
|
2
2
|
import { ECPairInterface } from 'ecpair';
|
|
3
3
|
import { Compressor } from '../../bytecode/Compressor.js';
|
|
4
4
|
import { EcKeyPair } from '../../keypair/EcKeyPair.js';
|
|
5
|
-
import { FeatureOpCodes, Features } from '../Features.js';
|
|
6
5
|
import { Generator } from '../Generator.js';
|
|
6
|
+
import { Feature, Features } from '../Features.js';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Class to generate bitcoin script for interaction transactions
|
|
@@ -63,13 +63,23 @@ export class LegacyCalldataGenerator extends Generator {
|
|
|
63
63
|
contractSecret: Buffer,
|
|
64
64
|
preimage: Buffer,
|
|
65
65
|
maxPriority: bigint,
|
|
66
|
-
features: Features[] = [],
|
|
66
|
+
features: Feature<Features>[] = [],
|
|
67
67
|
): Buffer {
|
|
68
68
|
const dataChunks: Buffer[][] = this.splitBufferIntoChunks(calldata);
|
|
69
69
|
if (!dataChunks.length) throw new Error('No data chunks found');
|
|
70
70
|
|
|
71
|
+
const featuresList: Features[] = [];
|
|
72
|
+
const featureData: (number | Buffer | Buffer[])[] = [];
|
|
73
|
+
for (let i = 0; i < features.length; i++) {
|
|
74
|
+
const feature = features[i];
|
|
75
|
+
featuresList.push(feature.opcode);
|
|
76
|
+
|
|
77
|
+
const data = this.encodeFeature(feature);
|
|
78
|
+
featureData.push(...data);
|
|
79
|
+
}
|
|
80
|
+
|
|
71
81
|
let compiledData = [
|
|
72
|
-
this.getHeader(maxPriority),
|
|
82
|
+
this.getHeader(maxPriority, featuresList),
|
|
73
83
|
opcodes.OP_TOALTSTACK,
|
|
74
84
|
|
|
75
85
|
// CHALLENGE PREIMAGE FOR REWARD,
|
|
@@ -94,11 +104,9 @@ export class LegacyCalldataGenerator extends Generator {
|
|
|
94
104
|
Generator.MAGIC,
|
|
95
105
|
];
|
|
96
106
|
|
|
97
|
-
const featureOpcodes = features.map((feature) => FeatureOpCodes[feature]); // Get the opcodes for the features
|
|
98
|
-
|
|
99
107
|
// Write calldata
|
|
100
108
|
compiledData = compiledData.concat(
|
|
101
|
-
...
|
|
109
|
+
...featureData,
|
|
102
110
|
...[opcodes.OP_1NEGATE, ...dataChunks, opcodes.OP_ELSE, opcodes.OP_1, opcodes.OP_ENDIF],
|
|
103
111
|
);
|
|
104
112
|
|
|
@@ -3,6 +3,7 @@ import { TransactionType } from '../enums/TransactionType.js';
|
|
|
3
3
|
import { TapLeafScript } from '../interfaces/Tap.js';
|
|
4
4
|
import { IInteractionParameters } from '../interfaces/ITransactionParameters.js';
|
|
5
5
|
import { SharedInteractionTransaction } from './SharedInteractionTransaction.js';
|
|
6
|
+
import { Feature, Features } from '../../generators/Features.js';
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* Class for interaction transactions
|
|
@@ -32,9 +33,23 @@ export class InteractionTransaction extends SharedInteractionTransaction<Transac
|
|
|
32
33
|
this.contractSecret,
|
|
33
34
|
this.preimage,
|
|
34
35
|
this.priorityFee,
|
|
36
|
+
this.generateFeatures(parameters),
|
|
35
37
|
);
|
|
36
38
|
|
|
37
39
|
this.scriptTree = this.getScriptTree();
|
|
38
40
|
this.internalInit();
|
|
39
41
|
}
|
|
42
|
+
|
|
43
|
+
private generateFeatures(parameters: IInteractionParameters): Feature<Features>[] {
|
|
44
|
+
const features: Feature<Features>[] = [];
|
|
45
|
+
|
|
46
|
+
if (parameters.loadedStorage) {
|
|
47
|
+
features.push({
|
|
48
|
+
opcode: Features.ACCESS_LIST,
|
|
49
|
+
data: parameters.loadedStorage,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return features;
|
|
54
|
+
}
|
|
40
55
|
}
|