Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add transaction_v1 group of functions #819

Merged
merged 4 commits into from
Sep 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion packages/core/src/rpc/rpc-spec/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import * as ChainHeadV1RPC from './chainHead_v1.js'
import * as TransactionV1RPC from './transaction_v1.js'

export { ChainHeadV1RPC }
export { ChainHeadV1RPC, TransactionV1RPC }

const handlers = {
...ChainHeadV1RPC,
...TransactionV1RPC,
}

export default handlers
33 changes: 33 additions & 0 deletions packages/core/src/rpc/rpc-spec/transaction_v1.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { Handler } from '../shared.js'
import { HexString } from '@polkadot/util/types'
import { defaultLogger } from '../../logger.js'

const logger = defaultLogger.child({ name: 'rpc-transaction_v1' })
const randomId = () => Math.random().toString(36).substring(2)

/**
* Submit the extrinsic to the transaction pool
*
* @param context
* @param params - [`extrinsic`]
*
* @return operation id
*/
export const transaction_v1_broadcast: Handler<[HexString], string | null> = async (context, [extrinsic]) => {
await context.chain.submitExtrinsic(extrinsic).catch((err) => {
// As per the spec, the invalid transaction errors should be ignored.
logger.warn('Submit extrinsic failed', err)
})

return randomId()
ermalkaleci marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* Stop broadcasting the transaction to other nodes.
*
*/
export const transaction_v1_stop: Handler<[string], null> = async (_context, [_operationId]) => {
// Chopsticks doesn't have any process to broadcast the transaction through P2P
// so stopping doesn't have any effect.
return null
}
3 changes: 2 additions & 1 deletion packages/e2e/src/helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ export const setupPolkadotApi = async (option: SetupOption) => {
chain: null as unknown as Blockchain,
substrateClient: null as unknown as SubstrateClient,
observableClient: null as unknown as ObservableClient,
ws: null as unknown as WsProvider,
}

beforeAll(async () => {
Expand All @@ -212,7 +213,7 @@ export const setupPolkadotApi = async (option: SetupOption) => {

beforeEach(async () => {
const res = await setup()
ws = res.ws
ws = result.ws = res.ws
chain = result.chain = res.chain
result.substrateClient = res.substrateClient
result.observableClient = res.observableClient
Expand Down
82 changes: 82 additions & 0 deletions packages/e2e/src/rpc-spec.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { ApiPromise } from '@polkadot/api'
import { RuntimeContext } from '@polkadot-api/observable-client'
import { describe, expect, it } from 'vitest'
import { dev, env, observe, setupPolkadotApi, testingPairs } from './helper.js'
import { firstValueFrom } from 'rxjs'

const testApi = await setupPolkadotApi(env.acalaV15)

const { alice, bob } = testingPairs()

describe('transaction_v1', async () => {
it('sends and executes transactions', async () => {
const chainHead = testApi.observableClient.chainHead$()

const api = await prepareChainForTx()

const TRANSFERRED_VALUE = 100n
const tx = await api.tx.balances.transferKeepAlive(bob.address, TRANSFERRED_VALUE).signAsync(alice)
xlc marked this conversation as resolved.
Show resolved Hide resolved
const { nextValue, subscription } = observe(chainHead.trackTx$(tx.toHex()))
const resultPromise = nextValue()
await new Promise((onSuccess, onError) =>
testApi.substrateClient._request('transaction_v1_broadcast', [tx.toHex()], { onSuccess, onError }),
)
const hash = await dev.newBlock()
ermalkaleci marked this conversation as resolved.
Show resolved Hide resolved

expect(await resultPromise).toMatchObject({
hash,
found: {
type: true,
},
})

const keyEncoder = (addr: string) => (ctx: RuntimeContext) =>
ctx.dynamicBuilder.buildStorage('System', 'Account').enc(addr)
const resultDecoder = (data: string | null, ctx: RuntimeContext) =>
data ? ctx.dynamicBuilder.buildStorage('System', 'Account').dec(data) : null
expect(
await firstValueFrom(chainHead.storage$(null, 'value', keyEncoder(bob.address), null, resultDecoder)),
).toMatchObject({
data: {
free: INITIAL_ACCOUNT_VALUE + TRANSFERRED_VALUE,
},
})

subscription.unsubscribe()
chainHead.unfollow()
})
})

const INITIAL_ACCOUNT_VALUE = 100_000_000_000_000n
async function prepareChainForTx() {
const api = await ApiPromise.create({
ermalkaleci marked this conversation as resolved.
Show resolved Hide resolved
provider: testApi.ws,
noInitWarn: true,
})
await api.isReady
await dev.setStorage({
System: {
Account: [
[
[alice.address],
{
providers: 1,
data: { free: INITIAL_ACCOUNT_VALUE },
},
],
[
[bob.address],
{
providers: 1,
data: { free: INITIAL_ACCOUNT_VALUE },
},
],
],
},
Sudo: {
Key: alice.address,
},
})

return api
}
Loading