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

feature: submit transaction #414

Merged
merged 2 commits into from
Feb 1, 2021
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
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