feat: interaction data item (#430)

This commit is contained in:
Asia
2023-06-28 16:03:27 +02:00
committed by GitHub
parent 977ab7ac61
commit 8924f7e961
10 changed files with 228 additions and 118 deletions

1
.gitignore vendored
View File

@@ -33,3 +33,4 @@ bundles/
logs logs
target target
metafile.json

View File

@@ -2,6 +2,8 @@ const { build } = require('esbuild');
const rimraf = require('rimraf'); const rimraf = require('rimraf');
const plugin = require('node-stdlib-browser/helpers/esbuild/plugin'); const plugin = require('node-stdlib-browser/helpers/esbuild/plugin');
const stdLibBrowser = require('node-stdlib-browser'); const stdLibBrowser = require('node-stdlib-browser');
const fs = require('fs');
const path = require('path');
const clean = async () => { const clean = async () => {
return new Promise((resolve) => { return new Promise((resolve) => {
@@ -24,7 +26,7 @@ const runBuild = async () => {
}; };
console.log('Building web bundle esm.'); console.log('Building web bundle esm.');
await build({ const result = await build({
...buildConfig, ...buildConfig,
minify: true, minify: true,
// metafile: true, // metafile: true,
@@ -34,6 +36,7 @@ const runBuild = async () => {
process.exit(1); process.exit(1);
}); });
// fs.writeFileSync(path.join(__dirname, 'metafile.json'), JSON.stringify(result.metafile));
console.log('Building web bundle iife.'); console.log('Building web bundle iife.');
await build({ await build({
...buildConfig, ...buildConfig,

View File

@@ -89,6 +89,7 @@
"safe-stable-stringify": "2.4.1", "safe-stable-stringify": "2.4.1",
"stream-buffers": "^3.0.2", "stream-buffers": "^3.0.2",
"unzipit": "^1.4.0", "unzipit": "^1.4.0",
"warp-arbundles": "1.0.1",
"warp-isomorphic": "1.0.4", "warp-isomorphic": "1.0.4",
"warp-wasm-metering": "1.0.1" "warp-wasm-metering": "1.0.1"
}, },
@@ -117,8 +118,8 @@
"ts-jest": "^28.0.7", "ts-jest": "^28.0.7",
"ts-node": "^10.2.1", "ts-node": "^10.2.1",
"typescript": "^4.9.5", "typescript": "^4.9.5",
"warp-contracts-plugin-deploy": "1.0.8-beta.0", "warp-contracts-plugin-deploy": "1.0.8",
"warp-contracts-plugin-vm2": "1.0.0", "warp-contracts-plugin-vm2": "1.0.1",
"warp-contracts-plugin-vrf": "^1.0.3", "warp-contracts-plugin-vrf": "^1.0.3",
"ws": "^8.11.0" "ws": "^8.11.0"
}, },

View File

@@ -43,9 +43,7 @@ describe('Wallet', () => {
it(`should throw for custom signing function and ethereum signature type`, () => { it(`should throw for custom signing function and ethereum signature type`, () => {
expect(() => { expect(() => {
new Signature(warp, { signer: sampleFunction, type: 'ethereum' }); new Signature(warp, { signer: sampleFunction, type: 'ethereum' });
}).toThrow( }).toThrow(`Unable to use signing function with signature of type: ethereum.`);
`Unable to use signing function of type: ethereum when not in mainnet environment or bundling is disabled.`
);
}); });
}); });
@@ -70,9 +68,18 @@ describe('Wallet', () => {
expect(sut.type).toStrictEqual('arweave'); expect(sut.type).toStrictEqual('arweave');
}); });
it(`should throw for custom signing function and arweave signature type`, () => {
expect(() => {
new Signature(warp, { signer: sampleFunction, type: 'arweave' });
}).toThrow(`Unable to use signing function when bundling is enabled.`);
});
});
describe('in testnet environment when gw url set to arweave', () => {
const warp = WarpFactory.forTestnet(defaultCacheOptions, true);
it(`should set correct signature for custom signing function and arweave signature type`, () => { it(`should set correct signature for custom signing function and arweave signature type`, () => {
const sut = new Signature(warp, { signer: sampleFunction, type: 'arweave' }); const sut = new Signature(warp, { signer: sampleFunction, type: 'arweave' });
expect(sut.signer).toEqual(sampleFunction); expect(sut.signer).toEqual(sampleFunction);
expect(sut.type).toEqual('arweave'); expect(sut.type).toEqual('arweave');
}); });
@@ -104,12 +111,10 @@ describe('Wallet', () => {
expect(sut.type).toEqual('arweave'); expect(sut.type).toEqual('arweave');
}); });
it(`should throw for custom signing function and arweave signature type`, () => { it(`should throw for custom signing function and ethereum signature type`, () => {
expect(() => { expect(() => {
const sut = new Signature(warp, { signer: sampleFunction, type: 'ethereum' }); const sut = new Signature(warp, { signer: sampleFunction, type: 'ethereum' });
}).toThrow( }).toThrow(`Unable to use signing function with signature of type: ethereum.`);
`Unable to use signing function of type: ethereum when not in mainnet environment or bundling is disabled.`
);
}); });
}); });
@@ -132,16 +137,16 @@ describe('Wallet', () => {
expect(sut.type).toStrictEqual('arweave'); expect(sut.type).toStrictEqual('arweave');
}); });
it(`should set correct signature for custom signing function and arweave signature type`, () => { it(`should throw for custom signing function and ethereum signature type`, () => {
const sut = new Signature(warp, { signer: sampleFunction, type: 'arweave' }); expect(() => {
expect(sut.signer.toString().replace(/\s+/g, '')).toEqual(sampleFunction.toString().replace(/\s+/g, '')); const sut = new Signature(warp, { signer: sampleFunction, type: 'ethereum' });
expect(sut.type).toEqual('arweave'); }).toThrow(`Unable to use signing function when bundling is enabled.`);
}); });
it(`should set correct signature for custom signing function and ethereum signature type`, () => { it(`should throw for custom signing function and arweave signature type`, () => {
const sut = new Signature(warp, { signer: sampleFunction, type: 'ethereum' }); expect(() => {
expect(sut.signer.toString().replace(/\s+/g, '')).toEqual(sampleFunction.toString().replace(/\s+/g, '')); const sut = new Signature(warp, { signer: sampleFunction, type: 'arweave' });
expect(sut.signer).toEqual(sampleFunction); }).toThrow(`Unable to use signing function when bundling is enabled.`);
}); });
}); });
@@ -157,9 +162,9 @@ describe('Wallet', () => {
}); });
it('should call getAddress for customSignature, if getAddress provided', async () => { it('should call getAddress for customSignature, if getAddress provided', async () => {
const warp = WarpFactory.forMainnet(); const warp = WarpFactory.forMainnet(defaultCacheOptions, true);
const customSignature: CustomSignature = { const customSignature: CustomSignature = {
type: 'ethereum', type: 'arweave',
signer: sampleFunction, signer: sampleFunction,
getAddress: () => Promise.resolve('owner') getAddress: () => Promise.resolve('owner')
}; };
@@ -171,39 +176,36 @@ describe('Wallet', () => {
}); });
it('should call getAddress for customSignature, if getAddress NOT provided', async () => { it('should call getAddress for customSignature, if getAddress NOT provided', async () => {
const warp = WarpFactory.forMainnet(); const warp = WarpFactory.forMainnet(defaultCacheOptions, true);
const customSignature: CustomSignature = { const customSignature: CustomSignature = {
type: 'ethereum', type: 'arweave',
signer: async (tx) => { signer: async (tx) => {
tx.owner = 'owner'; Promise.resolve(tx);
} }
}; };
const signature = new Signature(warp, customSignature); const signature = new Signature(warp, customSignature);
const address = await signature.getAddress(); const address = await signature.getAddress();
expect(address).toStrictEqual('owner'); expect(address).toStrictEqual('47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU');
}); });
it('should use cached valued from getAddress', async () => { it('should use cached valued from getAddress', async () => {
const warp = WarpFactory.forMainnet(); const warp = WarpFactory.forMainnet(defaultCacheOptions, true);
const mockedSigner = jest.fn(async (tx) => {
tx.owner = 'owner';
});
const customSignature: CustomSignature = { const customSignature: CustomSignature = {
type: 'ethereum', type: 'arweave',
signer: mockedSigner signer: async (tx) => {
Promise.resolve(tx);
}
}; };
const signature = new Signature(warp, customSignature); const signature = new Signature(warp, customSignature);
const address = await signature.getAddress(); const address = await signature.getAddress();
expect(address).toStrictEqual('owner'); expect(address).toStrictEqual('47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU');
const cachedAddress = await signature.getAddress(); const cachedAddress = await signature.getAddress();
expect(cachedAddress).toStrictEqual('owner'); expect(cachedAddress).toStrictEqual('47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU');
expect(mockedSigner).toBeCalledTimes(1);
}); });
}); });
}); });

View File

@@ -7,6 +7,7 @@ import { ArTransfer, Tags, ArWallet } from './deploy/CreateContract';
import { CustomSignature } from './Signature'; import { CustomSignature } from './Signature';
import { EvaluationOptionsEvaluator } from './EvaluationOptionsEvaluator'; import { EvaluationOptionsEvaluator } from './EvaluationOptionsEvaluator';
import { InteractionState } from './states/InteractionState'; import { InteractionState } from './states/InteractionState';
import { Signer } from 'warp-arbundles';
export type BenchmarkStats = { gatewayCommunication: number; stateEvaluation: number; total: number }; export type BenchmarkStats = { gatewayCommunication: number; stateEvaluation: number; total: number };
@@ -87,9 +88,9 @@ export interface Contract<State = unknown> {
* i.e. whether called contract's function required "caller" info) * i.e. whether called contract's function required "caller" info)
* Connecting a signer MUST be done before "writeInteraction" and "bundleInteraction". * Connecting a signer MUST be done before "writeInteraction" and "bundleInteraction".
* *
* @param signer - either {@link ArWallet} that will be connected to this contract or custom {@link SigningFunction} * @param signer - either {@link ArWallet} that will be connected to this contract, custom {@link SigningFunction} or {@link Signer}
*/ */
connect(signature: ArWallet | CustomSignature): Contract<State>; connect(signature: ArWallet | CustomSignature | Signer): Contract<State>;
/** /**
* Allows to set ({@link EvaluationOptions}) * Allows to set ({@link EvaluationOptions})

View File

@@ -14,13 +14,13 @@ import { InteractionsSorter } from '../core/modules/InteractionsSorter';
import { DefaultEvaluationOptions, EvalStateResult, EvaluationOptions } from '../core/modules/StateEvaluator'; import { DefaultEvaluationOptions, EvalStateResult, EvaluationOptions } from '../core/modules/StateEvaluator';
import { WARP_TAGS } from '../core/KnownTags'; import { WARP_TAGS } from '../core/KnownTags';
import { Warp } from '../core/Warp'; import { Warp } from '../core/Warp';
import { createDummyTx, createInteractionTx } from '../legacy/create-interaction-tx'; import { createDummyTx, createInteractionTagsList, createInteractionTx } from '../legacy/create-interaction-tx';
import { GQLNodeInterface } from '../legacy/gqlResult'; import { GQLNodeInterface } from '../legacy/gqlResult';
import { Benchmark } from '../logging/Benchmark'; import { Benchmark } from '../logging/Benchmark';
import { LoggerFactory } from '../logging/LoggerFactory'; import { LoggerFactory } from '../logging/LoggerFactory';
import { Evolve } from '../plugins/Evolve'; import { Evolve } from '../plugins/Evolve';
import { ArweaveWrapper } from '../utils/ArweaveWrapper'; import { ArweaveWrapper } from '../utils/ArweaveWrapper';
import { getJsonResponse, sleep, stripTrailingSlash } from '../utils/utils'; import { getJsonResponse, isBrowser, sleep, stripTrailingSlash } from '../utils/utils';
import { import {
BenchmarkStats, BenchmarkStats,
Contract, Contract,
@@ -35,12 +35,13 @@ import { CustomSignature, Signature } from './Signature';
import { EvaluationOptionsEvaluator } from './EvaluationOptionsEvaluator'; import { EvaluationOptionsEvaluator } from './EvaluationOptionsEvaluator';
import { WarpFetchWrapper } from '../core/WarpFetchWrapper'; import { WarpFetchWrapper } from '../core/WarpFetchWrapper';
import { Mutex } from 'async-mutex'; import { Mutex } from 'async-mutex';
import { TransactionStatusResponse } from '../utils/types/arweave-types'; import { Tag, TransactionStatusResponse } from '../utils/types/arweave-types';
import { InteractionState } from './states/InteractionState'; import { InteractionState } from './states/InteractionState';
import { ContractInteractionState } from './states/ContractInteractionState'; import { ContractInteractionState } from './states/ContractInteractionState';
import { Crypto } from 'warp-isomorphic'; import { Crypto } from 'warp-isomorphic';
import { VrfPluginFunctions } from '../core/WarpPlugin'; import { VrfPluginFunctions } from '../core/WarpPlugin';
import Arweave from 'arweave'; import Arweave from 'arweave';
import { createData, tagsExceedLimit, DataItem, Signer } from 'warp-arbundles';
/** /**
* An implementation of {@link Contract} that is backwards compatible with current style * An implementation of {@link Contract} that is backwards compatible with current style
@@ -48,6 +49,7 @@ import Arweave from 'arweave';
* *
* It requires {@link ExecutorFactory} that is using {@link HandlerApi} generic type. * It requires {@link ExecutorFactory} that is using {@link HandlerApi} generic type.
*/ */
export class HandlerBasedContract<State> implements Contract<State> { export class HandlerBasedContract<State> implements Contract<State> {
private readonly logger = LoggerFactory.INST.create('HandlerBasedContract'); private readonly logger = LoggerFactory.INST.create('HandlerBasedContract');
@@ -261,6 +263,7 @@ export class HandlerBasedContract<State> implements Contract<State> {
const bundleInteraction = interactionsLoader.type() == 'warp' && !effectiveDisableBundling; const bundleInteraction = interactionsLoader.type() == 'warp' && !effectiveDisableBundling;
this._signature.checkNonArweaveSigningAvailability(bundleInteraction); this._signature.checkNonArweaveSigningAvailability(bundleInteraction);
this._signature.checkBundlerSignerAvailability(bundleInteraction);
if ( if (
bundleInteraction && bundleInteraction &&
@@ -274,6 +277,10 @@ export class HandlerBasedContract<State> implements Contract<State> {
throw new Error('Vrf generation is only available for bundle interaction'); throw new Error('Vrf generation is only available for bundle interaction');
} }
if (!input) {
throw new Error(`Input should be a truthy value: ${JSON.stringify(input)}`);
}
if (bundleInteraction) { if (bundleInteraction) {
return await this.bundleInteraction(input, { return await this.bundleInteraction(input, {
tags: effectiveTags, tags: effectiveTags,
@@ -322,34 +329,86 @@ export class HandlerBasedContract<State> implements Contract<State> {
): Promise<WriteInteractionResponse | null> { ): Promise<WriteInteractionResponse | null> {
this.logger.info('Bundle interaction input', input); this.logger.info('Bundle interaction input', input);
const interactionTx = await this.createInteraction( const interactionDataItem = await this.createInteractionDataItem(
input, input,
options.tags, options.tags,
emptyTransfer, emptyTransfer,
options.strict, options.strict,
true,
options.vrf options.vrf
); );
const response = this._warpFetchWrapper.fetch( const response = this._warpFetchWrapper.fetch(
`${stripTrailingSlash(this._evaluationOptions.sequencerUrl)}/gateway/sequencer/register`, `${stripTrailingSlash(this._evaluationOptions.sequencerUrl)}/gateway/v2/sequencer/register`,
{ {
method: 'POST', method: 'POST',
body: JSON.stringify(interactionTx),
headers: { headers: {
'Accept-Encoding': 'gzip, deflate, br', 'Content-Type': 'application/octet-stream',
'Content-Type': 'application/json',
Accept: 'application/json' Accept: 'application/json'
} },
body: interactionDataItem.getRaw()
} }
); );
const dataItemId = await interactionDataItem.id;
return { return {
bundlrResponse: await getJsonResponse(response), bundlrResponse: await getJsonResponse(response),
originalTxId: interactionTx.id originalTxId: dataItemId
}; };
} }
private async createInteractionDataItem<Input>(
input: Input,
tags: Tags,
transfer: ArTransfer,
strict: boolean,
vrf = false
) {
if (this._evaluationOptions.internalWrites) {
// it modifies tags
await this.discoverInternalWrites<Input>(input, tags, transfer, strict, vrf);
}
if (vrf) {
tags.push(new Tag(WARP_TAGS.REQUEST_VRF, 'true'));
}
const interactionTags = createInteractionTagsList(
this._contractTxId,
input,
this.warp.environment === 'testnet',
tags
);
if (tagsExceedLimit(interactionTags)) {
throw new Error(`Interaction tags exceed limit of 4096 bytes.`);
}
const data = Math.random().toString().slice(-4);
const bundlerSigner = this._signature.bundlerSigner;
if (!bundlerSigner) {
throw new Error(
`Signer not set correctly. If you connect wallet through 'use_wallet', please remember that it only works when bundling is disabled.`
);
}
let interactionDataItem: DataItem;
if (isBrowser() && bundlerSigner.signer?.signDataItem) {
interactionDataItem = await bundlerSigner.signDataItem(data, interactionTags);
} else {
interactionDataItem = createData(data, bundlerSigner, { tags: interactionTags });
await interactionDataItem.sign(bundlerSigner);
}
// TODO: for ethereum owner is set to public key and not the address!!
if (!this._evaluationOptions.internalWrites && strict) {
await this.checkInteractionInStrictMode(interactionDataItem.owner, input, tags, transfer, strict, vrf);
}
return interactionDataItem;
}
private async createInteraction<Input>( private async createInteraction<Input>(
input: Input, input: Input,
tags: Tags, tags: Tags,
@@ -365,10 +424,7 @@ export class HandlerBasedContract<State> implements Contract<State> {
} }
if (vrf) { if (vrf) {
tags.push({ tags.push(new Tag(WARP_TAGS.REQUEST_VRF, 'true'));
name: WARP_TAGS.REQUEST_VRF,
value: 'true'
});
} }
const interactionTx = await createInteractionTx( const interactionTx = await createInteractionTx(
@@ -385,20 +441,28 @@ export class HandlerBasedContract<State> implements Contract<State> {
); );
if (!this._evaluationOptions.internalWrites && strict) { if (!this._evaluationOptions.internalWrites && strict) {
const { arweave } = this.warp; await this.checkInteractionInStrictMode(interactionTx.owner, input, tags, transfer, strict, vrf);
const caller =
this._signature.type == 'arweave'
? await arweave.wallets.ownerToAddress(interactionTx.owner)
: interactionTx.owner;
const handlerResult = await this.callContract(input, 'write', caller, undefined, tags, transfer, strict, vrf);
if (handlerResult.type !== 'ok') {
throw Error('Cannot create interaction: ' + JSON.stringify(handlerResult.error || handlerResult.errorMessage));
}
} }
return interactionTx; return interactionTx;
} }
private async checkInteractionInStrictMode<Input>(
owner: string,
input: Input,
tags: Tags,
transfer: ArTransfer,
strict: boolean,
vrf: boolean
) {
const { arweave } = this.warp;
const caller = this._signature.type == 'arweave' ? await arweave.wallets.ownerToAddress(owner) : owner;
const handlerResult = await this.callContract(input, 'write', caller, undefined, tags, transfer, strict, vrf);
if (handlerResult.type !== 'ok') {
throw Error('Cannot create interaction: ' + JSON.stringify(handlerResult.error || handlerResult.errorMessage));
}
}
txId(): string { txId(): string {
return this._contractTxId; return this._contractTxId;
} }
@@ -407,7 +471,7 @@ export class HandlerBasedContract<State> implements Contract<State> {
return this._callStack; return this._callStack;
} }
connect(signature: ArWallet | CustomSignature): Contract<State> { connect(signature: ArWallet | CustomSignature | Signer): Contract<State> {
this._signature = new Signature(this.warp, signature); this._signature = new Signature(this.warp, signature);
return this; return this;
} }
@@ -975,10 +1039,7 @@ export class HandlerBasedContract<State> implements Contract<State> {
this.logger.debug('Callstack', callStack.print()); this.logger.debug('Callstack', callStack.print());
innerWrites.forEach((contractTxId) => { innerWrites.forEach((contractTxId) => {
tags.push({ tags.push(new Tag(WARP_TAGS.INTERACT_WRITE, contractTxId));
name: WARP_TAGS.INTERACT_WRITE,
value: contractTxId
});
}); });
this.logger.debug('Tags with inner calls', tags); this.logger.debug('Tags with inner calls', tags);

View File

@@ -1,7 +1,9 @@
import { Warp } from '../core/Warp'; import { Warp } from '../core/Warp';
import { ArWallet } from './deploy/CreateContract'; import { ArWallet } from './deploy/CreateContract';
import { Transaction } from '../utils/types/arweave-types'; import { Transaction } from '../utils/types/arweave-types';
import { BundlerSigner } from './deploy/DataItem'; import { JWKInterface } from 'arweave/node/lib/wallet';
import { ArweaveSigner, Signer as BundlerSigner } from 'warp-arbundles';
import { isBrowser } from '../utils/utils';
export type SignatureType = 'arweave' | 'ethereum'; export type SignatureType = 'arweave' | 'ethereum';
export type SigningFunction = (tx: Transaction) => Promise<void>; export type SigningFunction = (tx: Transaction) => Promise<void>;
@@ -16,13 +18,13 @@ Different types which can be used to sign transaction or data item
- ArWallet - default option for signing Arweave transactions, either JWKInterface or 'use_wallet' - ArWallet - default option for signing Arweave transactions, either JWKInterface or 'use_wallet'
- CustomSignature - object with `signer` field - a custom signing function which takes transaction as a parameter and requires signing it - CustomSignature - object with `signer` field - a custom signing function which takes transaction as a parameter and requires signing it
on the client side and `type` field of type SignatureType which indicates the wallet's chain, either 'arweave' or 'ethereum' on the client side and `type` field of type SignatureType which indicates the wallet's chain, either 'arweave' or 'ethereum'
- Signer - arbundles specific class which allows to sign data items (only this type can be used when bundling is enabled and data items - Signer - arbundles specific class which allows to sign data items (works with data items - when bundling is enabled)
are being created)
*/ */
export type SignatureProvider = ArWallet | CustomSignature | BundlerSigner; export type SignatureProvider = ArWallet | CustomSignature | BundlerSigner;
export class Signature { export class Signature {
signer: SigningFunction; signer: SigningFunction;
bundlerSigner: BundlerSigner;
readonly type: SignatureType; readonly type: SignatureType;
readonly warp: Warp; readonly warp: Warp;
private readonly signatureProviderType: 'CustomSignature' | 'ArWallet' | 'BundlerSigner'; private readonly signatureProviderType: 'CustomSignature' | 'ArWallet' | 'BundlerSigner';
@@ -40,8 +42,11 @@ export class Signature {
} else if (this.isValidBundlerSignature(walletOrSignature)) { } else if (this.isValidBundlerSignature(walletOrSignature)) {
this.signatureProviderType = 'BundlerSigner'; this.signatureProviderType = 'BundlerSigner';
this.type = decodeBundleSignatureType(walletOrSignature.signatureType); this.type = decodeBundleSignatureType(walletOrSignature.signatureType);
this.bundlerSigner = walletOrSignature;
} else { } else {
this.assignArweaveSigner(walletOrSignature); this.assignArweaveSigner(walletOrSignature);
this.bundlerSigner =
typeof walletOrSignature == 'string' ? null : new ArweaveSigner(walletOrSignature as JWKInterface);
this.signatureProviderType = 'ArWallet'; this.signatureProviderType = 'ArWallet';
this.type = 'arweave'; this.type = 'arweave';
} }
@@ -76,19 +81,22 @@ export class Signature {
} }
} }
private async deduceSignerBySigning() { private async deduceSignerBySigning(): Promise<string> {
const { arweave } = this.warp; const { arweave } = this.warp;
const dummyTx = await arweave.createTransaction({ if (this.signatureProviderType == 'BundlerSigner') {
data: Math.random().toString().slice(-4), try {
reward: '72600854', return await this.bundlerSigner.getAddress();
last_tx: 'p7vc1iSP6bvH_fCeUFa9LqoV5qiyW-jdEKouAT0XMoSwrNraB9mgpi29Q10waEpO' } catch (e) {
}); throw new Error(`Could not get address from the signer. Is the 'getAddress' implementation correct?'`);
await this.signer(dummyTx); }
} else if (this.signatureProviderType == 'ArWallet' || this.signatureProviderType == 'CustomSignature') {
if (this.type === 'ethereum') { const dummyTx = await arweave.createTransaction({
return dummyTx.owner; data: Math.random().toString().slice(-4),
} else if (this.type === 'arweave') { reward: '72600854',
last_tx: 'p7vc1iSP6bvH_fCeUFa9LqoV5qiyW-jdEKouAT0XMoSwrNraB9mgpi29Q10waEpO'
});
await (this.signer as SigningFunction)(dummyTx);
return arweave.wallets.ownerToAddress(dummyTx.owner); return arweave.wallets.ownerToAddress(dummyTx.owner);
} else { } else {
throw Error('Unknown Signature::type'); throw Error('Unknown Signature::type');
@@ -101,6 +109,18 @@ export class Signature {
} }
} }
checkBundlerSignerAvailability(bundling: boolean): void {
if (bundling && isBrowser() && this.signatureProviderType != 'BundlerSigner') {
throw new Error(`Only wallet of type 'Signer' is allowed when bundling is enabled and in browser.`);
}
if ((!bundling || this.warp.environment == 'local') && this.signatureProviderType == 'BundlerSigner') {
throw new Error(
`Only wallet of type 'ArWallet' or 'CustomSignature' is allowed when bundling is disabled or in local environment.`
);
}
}
private assignArweaveSigner(walletOrSignature) { private assignArweaveSigner(walletOrSignature) {
this.signer = async (tx: Transaction) => { this.signer = async (tx: Transaction) => {
await this.warp.arweave.transactions.sign(tx, walletOrSignature); await this.warp.arweave.transactions.sign(tx, walletOrSignature);
@@ -108,19 +128,17 @@ export class Signature {
} }
private assertEnvForCustomSigner(signatureType: SignatureType) { private assertEnvForCustomSigner(signatureType: SignatureType) {
if (signatureType === 'arweave') { if (this.warp.interactionsLoader.type() === 'warp') {
return; throw new Error(`Unable to use signing function when bundling is enabled.`);
} }
if (!['mainnet', 'testnet'].includes(this.warp.environment) || this.warp.interactionsLoader.type() !== 'warp') { if (signatureType == 'ethereum') {
throw new Error( throw new Error(`Unable to use signing function with signature of type: ${signatureType}.`);
`Unable to use signing function of type: ${signatureType} when not in mainnet environment or bundling is disabled.`
);
} }
} }
private isCustomSignature(signature: SignatureProvider): signature is CustomSignature { private isCustomSignature(signature: SignatureProvider): signature is CustomSignature {
return (signature as CustomSignature).signer !== undefined; return (signature as CustomSignature).signer !== undefined && (signature as CustomSignature).type !== undefined;
} }
private isValidBundlerSignature(signature: SignatureProvider): signature is BundlerSigner { private isValidBundlerSignature(signature: SignatureProvider): signature is BundlerSigner {

View File

@@ -4,8 +4,9 @@ import { EvaluationOptions } from '../../core/modules/StateEvaluator';
import { Source } from './Source'; import { Source } from './Source';
import { BundlerSigner } from './DataItem'; import { BundlerSigner } from './DataItem';
import { CustomSignature } from '../Signature'; import { CustomSignature } from '../Signature';
import { Tag } from '../../utils/types/arweave-types';
export type Tags = { name: string; value: string }[]; export type Tags = Tag[];
export type ArWallet = JWKInterface | 'use_wallet'; export type ArWallet = JWKInterface | 'use_wallet';

View File

@@ -3,14 +3,15 @@ import { SMART_WEAVE_TAGS, WARP_TAGS } from '../core/KnownTags';
import { GQLNodeInterface } from './gqlResult'; import { GQLNodeInterface } from './gqlResult';
import { TagsParser } from '../core/modules/impl/TagsParser'; import { TagsParser } from '../core/modules/impl/TagsParser';
import { SigningFunction } from '../contract/Signature'; import { SigningFunction } from '../contract/Signature';
import { BlockData, CreateTransactionInterface, Transaction } from '../utils/types/arweave-types'; import { BlockData, CreateTransactionInterface, Tag, Transaction } from '../utils/types/arweave-types';
import { Tags } from '../contract/deploy/CreateContract';
export async function createInteractionTx<Input>( export async function createInteractionTx<Input>(
arweave: Arweave, arweave: Arweave,
signer: SigningFunction, signer: SigningFunction,
contractId: string, contractId: string,
input: Input, input: Input,
tags: { name: string; value: string }[], tags: Tags,
target = '', target = '',
winstonQty = '0', winstonQty = '0',
dummy = false, dummy = false,
@@ -42,24 +43,8 @@ export async function createInteractionTx<Input>(
const interactionTx = await arweave.createTransaction(options); const interactionTx = await arweave.createTransaction(options);
if (!input) { const interactionTags = createInteractionTagsList(contractId, input, isTestnet, tags);
throw new Error(`Input should be a truthy value: ${JSON.stringify(input)}`); interactionTags.forEach((t) => interactionTx.addTag(t.name, t.value));
}
if (tags && tags.length) {
for (const tag of tags) {
interactionTx.addTag(tag.name.toString(), tag.value.toString());
}
}
interactionTx.addTag(SMART_WEAVE_TAGS.APP_NAME, 'SmartWeaveAction');
// use real SDK version here?
interactionTx.addTag(SMART_WEAVE_TAGS.APP_VERSION, '0.3.0');
interactionTx.addTag(SMART_WEAVE_TAGS.SDK, 'Warp');
interactionTx.addTag(SMART_WEAVE_TAGS.CONTRACT_TX_ID, contractId);
interactionTx.addTag(SMART_WEAVE_TAGS.INPUT, JSON.stringify(input));
if (isTestnet) {
interactionTx.addTag(WARP_TAGS.WARP_TESTNET, '1.0.0');
}
if (signer) { if (signer) {
await signer(interactionTx); await signer(interactionTx);
@@ -114,3 +99,30 @@ export function createDummyTx(tx: Transaction, from: string, block: BlockData):
bundledIn: null bundledIn: null
}; };
} }
export function createInteractionTagsList<Input>(
contractId: string,
input: Input,
isTestnet: boolean,
customTags?: Tags
) {
const interactionTags: Tags = [];
if (customTags && customTags.length) {
for (const customTag of customTags) {
interactionTags.push(new Tag(customTag.name.toString(), customTag.value.toString()));
}
}
interactionTags.push(new Tag(SMART_WEAVE_TAGS.APP_NAME, 'SmartWeaveAction'));
// use real SDK version here?
interactionTags.push(new Tag(SMART_WEAVE_TAGS.APP_VERSION, '0.3.0'));
interactionTags.push(new Tag(SMART_WEAVE_TAGS.SDK, 'Warp'));
interactionTags.push(new Tag(SMART_WEAVE_TAGS.CONTRACT_TX_ID, contractId));
interactionTags.push(new Tag(SMART_WEAVE_TAGS.INPUT, JSON.stringify(input)));
if (isTestnet) {
interactionTags.push(new Tag(WARP_TAGS.WARP_TESTNET, '1.0.0'));
}
return interactionTags;
}

View File

@@ -2365,7 +2365,7 @@ arweave-stream-tx@^1.1.0:
dependencies: dependencies:
exponential-backoff "^3.1.0" exponential-backoff "^3.1.0"
arweave@1.13.7: arweave@1.13.7, arweave@^1.13.7:
version "1.13.7" version "1.13.7"
resolved "https://registry.yarnpkg.com/arweave/-/arweave-1.13.7.tgz#cda8534c833baec372a7052c61f53b4e39a886d7" resolved "https://registry.yarnpkg.com/arweave/-/arweave-1.13.7.tgz#cda8534c833baec372a7052c61f53b4e39a886d7"
integrity sha512-Hv+x2bSI6UyBHpuVbUDMMpMje1ETfpJWj52kKfz44O0IqDRi/LukOkkDUptup1p6OT6KP1/DdpnUnsNHoskFeA== integrity sha512-Hv+x2bSI6UyBHpuVbUDMMpMje1ETfpJWj52kKfz44O0IqDRi/LukOkkDUptup1p6OT6KP1/DdpnUnsNHoskFeA==
@@ -7472,19 +7472,29 @@ walker@^1.0.8:
dependencies: dependencies:
makeerror "1.0.12" makeerror "1.0.12"
warp-contracts-plugin-deploy@1.0.8-beta.0: warp-arbundles@1.0.1:
version "1.0.8-beta.0" version "1.0.1"
resolved "https://registry.yarnpkg.com/warp-contracts-plugin-deploy/-/warp-contracts-plugin-deploy-1.0.8-beta.0.tgz#c4c95a57d53a5ae2a50c0907f94ac74e6bcdbada" resolved "https://registry.yarnpkg.com/warp-arbundles/-/warp-arbundles-1.0.1.tgz#c816ee44aacd90e0214b1b7db8028c6d5b468770"
integrity sha512-bA8K9QHuIvE1XzWpFYmAR5+xisanAzfInxx0ekMwlGVFiDWciWcVAFNefLUa5iFHuHlDpqgDQvUpsd6lg7Wx8g== integrity sha512-bWyF8PoS5D2M8yk7YAT/DFi4pzoPxfXaS1o9eEelcD5SU42PW/wVxwzrvn6pLaHFIK5Zys15V2eGd+8X1Ty62Q==
dependencies:
arweave "^1.13.7"
base64url "^3.0.1"
buffer "^6.0.3"
warp-isomorphic "^1.0.4"
warp-contracts-plugin-deploy@1.0.8:
version "1.0.8"
resolved "https://registry.yarnpkg.com/warp-contracts-plugin-deploy/-/warp-contracts-plugin-deploy-1.0.8.tgz#94e9841ea3f68d09e9c1bbd483efa84a2c541901"
integrity sha512-5KF12X0g2L1BrGGWGidkJAex3AK0c3y3KQDT1J/5OGCmldgesogKOKADw4VrzcdOvV/CbjdyiIXy5XB9cYXPGQ==
dependencies: dependencies:
arbundles "^0.7.3" arbundles "^0.7.3"
arlocal "^1.1.59" arlocal "^1.1.59"
node-stdlib-browser "^1.2.0" node-stdlib-browser "^1.2.0"
warp-contracts-plugin-vm2@1.0.0: warp-contracts-plugin-vm2@1.0.1:
version "1.0.0" version "1.0.1"
resolved "https://registry.yarnpkg.com/warp-contracts-plugin-vm2/-/warp-contracts-plugin-vm2-1.0.0.tgz#f55914a228102f3bda7dafc197c395078fa0e190" resolved "https://registry.yarnpkg.com/warp-contracts-plugin-vm2/-/warp-contracts-plugin-vm2-1.0.1.tgz#0c1749f998508e8559a4627afc574ff80577353c"
integrity sha512-3XubaZ9eeRbBtMDwmTAewZk6WYgtcg9g44vATqH5vmn2wdPWIBcrBX3gA7XQIDbqPcLPpIgifMDZiQJSNwYZDA== integrity sha512-ogECglCyZZ3vF7nm9RqkDA+b8AwHmoda8Uv2oYIMyxm7haLjTMtZNY+QvqDbI3b/B/MR+nX1nwdTe7KIUqqQjQ==
dependencies: dependencies:
bignumber.js "^9.1.1" bignumber.js "^9.1.1"
vm2 "^3.9.16" vm2 "^3.9.16"
@@ -7505,7 +7515,7 @@ warp-isomorphic@1.0.0:
buffer "^6.0.3" buffer "^6.0.3"
undici "^5.8.0" undici "^5.8.0"
warp-isomorphic@1.0.4: warp-isomorphic@1.0.4, warp-isomorphic@^1.0.4:
version "1.0.4" version "1.0.4"
resolved "https://registry.yarnpkg.com/warp-isomorphic/-/warp-isomorphic-1.0.4.tgz#1017eba260e0f0228b33b94b9e36a1afe54e09d8" resolved "https://registry.yarnpkg.com/warp-isomorphic/-/warp-isomorphic-1.0.4.tgz#1017eba260e0f0228b33b94b9e36a1afe54e09d8"
integrity sha512-W77IoLjq/eu5bY1uRrlmVt5lLDoIHeZ0ozJ/j67FTnxvZRXu887biEnom1nx8q1UgOKyJh8eQYFQaE2FLlKhFg== integrity sha512-W77IoLjq/eu5bY1uRrlmVt5lLDoIHeZ0ozJ/j67FTnxvZRXu887biEnom1nx8q1UgOKyJh8eQYFQaE2FLlKhFg==