Skip to content

Commit

Permalink
Merge pull request #414 from input-output-hk/feature/submit-transaction
Browse files Browse the repository at this point in the history
feature: submit transaction
  • Loading branch information
rhyslbw authored Feb 1, 2021
2 parents 4615ded + 9e8e1cc commit 37c6233
Show file tree
Hide file tree
Showing 14 changed files with 125 additions and 6 deletions.
Binary file not shown.
Binary file added packages-cache/@types-temp-write-4.0.0.tgz
Binary file not shown.
Binary file added packages-cache/temp-dir-1.0.0.tgz
Binary file not shown.
Binary file added packages-cache/temp-write-4.0.0.tgz
Binary file not shown.
3 changes: 3 additions & 0 deletions packages/api-cardano-db-hasura/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
],
"dependencies": {
"@cardano-graphql/util": "3.1.0",
"@emurgo/cardano-serialization-lib-nodejs": "^5.0.0-rc.0",
"@graphql-tools/delegate": "^6.0.10",
"@graphql-tools/schema": "^6.0.9",
"@graphql-tools/wrap": "^6.0.9",
Expand All @@ -51,6 +52,7 @@
"pg": "^8.5.1",
"pg-listen": "^1.6.0",
"set-interval-async": "^1.0.33",
"temp-write": "^4.0.0",
"ts-log": "^2.2.3"
},
"devDependencies": {
Expand All @@ -62,6 +64,7 @@
"@types/node": "^14.0.13",
"@types/pg": "^7.14.4",
"@types/set-interval-async": "^1.0.0",
"@types/temp-write": "^4.0.0",
"shx": "^0.3.2",
"typescript": "^3.9.5"
}
Expand Down
10 changes: 10 additions & 0 deletions packages/api-cardano-db-hasura/schema.graphql
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
schema {
mutation: Mutation
query: Query
}

Expand All @@ -16,6 +17,11 @@ scalar Timestamp
scalar URL
scalar VRFVerificationKey

type Mutation {
# Submit a signed transaction to the network
submitTransaction (transaction: String!): TransactionSubmitResponse!
}

type Query {
activeStake (
limit: Int
Expand Down Expand Up @@ -1044,6 +1050,10 @@ type TransactionOutput_sum_fields {
value: String
}

type TransactionSubmitResponse {
hash: String!
}

type Block {
# Genesis block does not belong to the 0th epoch, therefore it could be null
epoch: Epoch
Expand Down
22 changes: 18 additions & 4 deletions packages/api-cardano-db-hasura/src/CardanoCli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { exec } from 'child_process'
import { Genesis, ShelleyProtocolParams } from './graphql_types'
import { Config } from './Config'
import { LedgerState } from './CardanoNodeClient'
import { knownEras } from '@cardano-graphql/util'
import { knownEras, capitalizeFirstChar } from '@cardano-graphql/util'

export interface CardanoCliTip {
blockNo: number,
Expand All @@ -14,14 +14,15 @@ export type ProtocolParams = ShelleyProtocolParams
const isEraMismatch = (errorMessage: string, era: string): boolean => {
return errorMessage.includes('EraMismatch') ||
errorMessage.includes(
`The attempted local state query does not support the ${era.charAt(0).toUpperCase().concat(era.slice(1))} protocol`
`The attempted local state query does not support the ${capitalizeFirstChar(era)} protocol`
)
}

export interface CardanoCli {
getLedgerState(): Promise<LedgerState>,
getProtocolParams(): Promise<ProtocolParams>,
getTip(): Promise<CardanoCliTip>
getTip(): Promise<CardanoCliTip>,
submitTransaction(filePath: string): Promise<void>
}

export function createCardanoCli (
Expand Down Expand Up @@ -75,6 +76,19 @@ export function createCardanoCli (
withEraFlag: true
}
),
getTip: () => query<CardanoCliTip>('tip')
getTip: () => query<CardanoCliTip>('tip'),
submitTransaction: (filePath) => {
return new Promise((resolve, reject) => {
exec(
`${cardanoCliPath} transaction submit --tx-file ${filePath} ${networkArg}`,
(error, _stdout, stderr) => {
if (error !== null || stderr.toString() !== '') {
return reject(new Error(stderr.toString()))
}
return resolve()
}
)
})
}
}
}
45 changes: 43 additions & 2 deletions packages/api-cardano-db-hasura/src/CardanoNodeClient.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { CardanoCli } from './CardanoCli'
import fs from 'fs-extra'
import { AssetSupply } from './graphql_types'
import { AssetSupply, Transaction } from './graphql_types'
import pRetry from 'p-retry'
import util, { DataFetcher } from '@cardano-graphql/util'
import util, { DataFetcher, knownEras } from '@cardano-graphql/util'
import tempWrite from 'temp-write'
import { dummyLogger, Logger } from 'ts-log'
import { getHashOfSignedTransaction } from './util'

export type LedgerState = {
accountState: {
Expand All @@ -15,6 +17,23 @@ export type LedgerState = {
}
}

const fileTypeFromEra = (era: string) => {
switch (era) {
case 'mary' :
return 'Tx MaryEra'
case 'allegra' :
return 'Tx AllegraEra'
case 'shelley' :
return 'TxSignedShelley'
default :
throw new Error(`Transaction not submitted. ${era} era not supported.`)
}
}

const isEraMismatch = (errorMessage: string): boolean =>
errorMessage.includes('DecoderErrorDeserialiseFailure') ||
errorMessage.includes('The era of the node and the tx do not match')

export class CardanoNodeClient {
readonly networkParams: string[]
public adaCirculatingSupply: AssetSupply['circulating']
Expand Down Expand Up @@ -79,4 +98,26 @@ export class CardanoNodeClient {
public async shutdown () {
await this.ledgerStateFetcher.shutdown()
}

public async submitTransaction (transaction: string): Promise<Transaction['hash']> {
for (const era of knownEras) {
const filePath = await tempWrite(`{
"type": "${fileTypeFromEra(era)}",
"description": "",
"cborHex": "${transaction}"
}`)
const hash = getHashOfSignedTransaction(transaction)
try {
await this.cardanoCli.submitTransaction(filePath)
this.logger.info('submitTransaction', { module: 'CardanoNodeClient', hash: hash })
return hash
} catch (error) {
if (!isEraMismatch(error.message)) {
throw error
}
} finally {
await fs.unlink(filePath)
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
mutation submitTransaction(
$transaction: String!
) {
submitTransaction(transaction: $transaction)
}
7 changes: 7 additions & 0 deletions packages/api-cardano-db-hasura/src/executableSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,13 @@ export async function buildSchema (
}
return makeExecutableSchema({
resolvers: Object.assign({}, scalarResolvers, {
Mutation: {
submitTransaction: async (_root, args) => {
await throwIfNotInCurrentEra('submitTransaction')
const hash = await cardanoNodeClient.submitTransaction(args.transaction)
return { hash }
}
},
PaymentAddress: {
summary: async (parent, args) => {
try {
Expand Down
8 changes: 8 additions & 0 deletions packages/api-cardano-db-hasura/src/util.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import CardanoWasm from '@emurgo/cardano-serialization-lib-nodejs'
import { Config } from './Config'
import fs from 'fs-extra'
import path from 'path'
Expand All @@ -9,3 +10,10 @@ export async function readSecrets (rootDir: string): Promise<Partial<Config['db'
user: (await fs.readFile(path.join(rootDir, 'postgres_user'), 'utf8')).toString()
}
}

export function getHashOfSignedTransaction (signedTransaction: string): string {
const signedTransactionBytes = Buffer.from(signedTransaction, 'hex')
const parsed = CardanoWasm.Transaction.from_bytes(signedTransactionBytes)
const hashBuffer = parsed && parsed.body() && Buffer.from(CardanoWasm.hash_transaction(parsed.body()).to_bytes())
return hashBuffer.toString('hex')
}
1 change: 1 addition & 0 deletions packages/util/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { loadQueryNode } from './queryNodeLoading'
import * as scalars from './scalars'
export * from './data_fetching'
export * from './knownEras'
export * from './stringModifiers'

export default {
onFailedAttemptFor,
Expand Down
2 changes: 2 additions & 0 deletions packages/util/src/stringModifiers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@

export const capitalizeFirstChar = (word: string): string => word.charAt(0).toUpperCase().concat(word.slice(1))
28 changes: 28 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -574,6 +574,11 @@
exec-sh "^0.3.2"
minimist "^1.2.0"

"@emurgo/cardano-serialization-lib-nodejs@^5.0.0-rc.0":
version "5.0.0-rc.0"
resolved "https://registry.yarnpkg.com/@emurgo/cardano-serialization-lib-nodejs/-/cardano-serialization-lib-nodejs-5.0.0-rc.0.tgz#da6e7679ee83e467adfc78c2789b2a5129f7f6b1"
integrity sha512-CVd5YHVIsxiokJTYnyZunzr8jaeuEMVb83Jjt4j0OTNJJfp3ReV0ZIvGsoGJUP2K+07nT8xWnHDEDrMH97ZIkQ==

"@graphql-codegen/cli@^1.15.2":
version "1.16.2"
resolved "https://registry.yarnpkg.com/@graphql-codegen/cli/-/cli-1.16.2.tgz#ff92e5e6813a404616d7504500be26ff88b7092d"
Expand Down Expand Up @@ -1699,6 +1704,13 @@
resolved "https://registry.yarnpkg.com/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz#9aa30c04db212a9a0649d6ae6fd50accc40748a1"
integrity sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==

"@types/temp-write@^4.0.0":
version "4.0.0"
resolved "https://registry.yarnpkg.com/@types/temp-write/-/temp-write-4.0.0.tgz#983237bb6dbcac883137dba8189b8c6177e8a04d"
integrity sha512-BNcDNG/ujXmzR49TeVxcWMbglOO55YQbe1ij3LE6WoZdLtZvclSEl5GAmmlfsr0Y5kM9mLIE1wk3r3pzQz62gQ==
dependencies:
temp-write "*"

"@types/through@*":
version "0.0.30"
resolved "https://registry.yarnpkg.com/@types/through/-/through-0.0.30.tgz#e0e42ce77e897bd6aead6f6ea62aeb135b8a3895"
Expand Down Expand Up @@ -8676,6 +8688,22 @@ tdigest@^0.1.1:
dependencies:
bintrees "1.0.1"

temp-dir@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/temp-dir/-/temp-dir-1.0.0.tgz#0a7c0ea26d3a39afa7e0ebea9c1fc0bc4daa011d"
integrity sha1-CnwOom06Oa+n4OvqnB/AvE2qAR0=

temp-write@*, temp-write@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/temp-write/-/temp-write-4.0.0.tgz#cd2e0825fc826ae72d201dc26eef3bf7e6fc9320"
integrity sha512-HIeWmj77uOOHb0QX7siN3OtwV3CTntquin6TNVg6SHOqCP3hYKmox90eeFOGaY1MqJ9WYDDjkyZrW6qS5AWpbw==
dependencies:
graceful-fs "^4.1.15"
is-stream "^2.0.0"
make-dir "^3.0.0"
temp-dir "^1.0.0"
uuid "^3.3.2"

terminal-link@^2.0.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/terminal-link/-/terminal-link-2.1.1.tgz#14a64a27ab3c0df933ea546fba55f2d078edc994"
Expand Down

0 comments on commit 37c6233

Please sign in to comment.