-
Notifications
You must be signed in to change notification settings - Fork 166
Simple mina-signer integration test app #2646
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
Open
dkijania
wants to merge
3
commits into
main
Choose a base branch
from
dkijania/mina-test-signer
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+534
−0
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| # Test Signer CLI | ||
|
|
||
| Command-line helper for drafting, signing, and broadcasting Mina payments via | ||
| the public GraphQL API. It wraps the `mina-test-signer` library so you can submit | ||
| transactions without wiring up a full wallet or SDK. | ||
|
|
||
| ## Getting Started | ||
|
|
||
| - **Prerequisites:** Node.js 18+ (for native `fetch`) and npm. | ||
| - **Install dependencies:** `npm install` | ||
| - **Quick run:** | ||
| `node mina-test-signer.js <private_key> <recipient_address> [graphql_url] [nonce]` | ||
|
|
||
| The optional `graphql_url` flag lets you override the default target defined in | ||
| `config.js`. | ||
|
|
||
| ## Workflow | ||
|
|
||
| 1. `mina-test-signer.js` parses CLI arguments and wires the supporting services. | ||
| 2. `graphql-client.js` sends the signed payload to the Mina daemon and can check | ||
| whether the transaction reached the pool. | ||
| 3. `utils.js` provides small helpers for GraphQL string construction and CLI | ||
| validation. | ||
| 4. `config.js` centralises network defaults and usage messaging. | ||
|
|
||
| Check the console output for a transaction id; you can re-run the pool check or | ||
| the `getPooledUserCommands` helper to confirm inclusion. Provide a `nonce` | ||
| argument when you need to synchronise with on-chain account state manually. The | ||
| CLI prints emoji-enhanced step logs and a summary table so you can spot | ||
| successes and failures at a glance. GraphQL errors (including malformed | ||
| responses) cause the CLI to exit with a non-zero status so they can be surfaced | ||
| in scripts and CI. | ||
|
|
||
| ## Private key format | ||
|
|
||
| For clarity, private key is in output format of: | ||
|
|
||
| ``` | ||
| mina advanced dump-keypair --privkey-path ... | ||
| ``` | ||
|
|
||
| ## Safety Notes | ||
|
|
||
| - Treat private keys in plain text with care. Prefer environment variables or a | ||
| secure secrets manager for real deployments. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| /** | ||
| * Centralized configuration for Mina payment signing. | ||
| * Keeps network defaults and unit conversion helpers in one place so | ||
| * the rest of the code can remain declarative. | ||
| */ | ||
| export const CONFIG = { | ||
| NETWORK: 'testnet', | ||
| DEFAULT_GRAPHQL_URL: 'http://localhost:3085/graphql', | ||
| MINA_UNITS: { | ||
| ONE_MINA: 1000000000, | ||
| DEFAULT_AMOUNT_MULTIPLIER: 150, | ||
| DEFAULT_FEE_MULTIPLIER: 1 | ||
| } | ||
| }; | ||
|
|
||
| /** | ||
| * Human-friendly CLI usage text that `mina-test-signer.js` displays when | ||
| * the caller provides incomplete arguments. | ||
| */ | ||
| export const USAGE_INFO = { | ||
| message: 'Usage: node mina-test-signer.js <private_key> <recipient_address> [graphql_url] [nonce]', | ||
| example: 'Example: node mina-test-signer.js EKErBK1KznrJJY3raJafSyxSayJ6viejaVrmjzXkSmoxXiJQsesU B62qp4wcxoJyFFyXZ2RVw8kGPpWn6ncK4RtsTz29jFf6fY2XYN42R1v http://172.17.0.3:3085/graphql 3', | ||
| defaultUrl: `Default GraphQL URL: ${CONFIG.DEFAULT_GRAPHQL_URL}` | ||
| }; | ||
177 changes: 177 additions & 0 deletions
177
src/mina-signer/tests/mina-signer-test-app/graphql-client.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,177 @@ | ||
| import { GraphQLUtils } from './utils.js'; | ||
|
|
||
| /** | ||
| * Minimal GraphQL transport layer responsible for broadcasting signed | ||
| * payments to a Mina daemon and inspecting the transaction pool. | ||
| */ | ||
| export class GraphQLClient { | ||
| constructor(url) { | ||
| this.url = url; | ||
| } | ||
|
|
||
| /** | ||
| * Posts a signed payment mutation to the configured GraphQL endpoint. | ||
| * Surfaces detailed errors while preserving the structured response | ||
| * the caller uses to confirm transaction submission. | ||
| */ | ||
| async sendPayment(signedPayment) { | ||
| const query = GraphQLUtils.createPaymentMutation(signedPayment); | ||
|
|
||
| console.log('\n🚀 Sending payment via GraphQL'); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i am personally not a big fan of heavy emoji use in our code bsae but i dont want to block this PR because of that |
||
| console.log(`🌐 Endpoint: ${this.url}`); | ||
| console.log('📝 Mutation payload:'); | ||
| console.log(query); | ||
|
|
||
| try { | ||
| const response = await fetch(this.url, { | ||
| method: 'POST', | ||
| headers: { 'Content-Type': 'application/json' }, | ||
| body: JSON.stringify({ query }), | ||
| }); | ||
|
|
||
| return await this.handleResponse(response); | ||
| } catch (error) { | ||
| throw new Error(`Request error: ${error.message}`); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Normalizes the GraphQL response shape by either returning JSON data | ||
| * or throwing a rich error that upstream callers can surface. | ||
| */ | ||
| async handleResponse(response) { | ||
| if (response.status === 200) { | ||
| const rawBody = await response.text(); | ||
|
|
||
| let json; | ||
| try { | ||
| json = JSON.parse(rawBody); | ||
| } catch (parseError) { | ||
| throw new Error( | ||
| `Unexpected JSON payload: ${parseError.message}. Raw response: ${rawBody}` | ||
| ); | ||
| } | ||
|
|
||
| if (json.errors?.length) { | ||
| const combinedErrors = json.errors | ||
| .map(error => error.message ?? JSON.stringify(error)) | ||
| .join(' | '); | ||
| throw new Error(`GraphQL errors: ${combinedErrors}`); | ||
| } | ||
|
|
||
| console.log('📦 GraphQL response payload:'); | ||
| console.dir(json, { depth: null }); | ||
| return json; | ||
| } else { | ||
| const text = await response.text(); | ||
| throw new Error(`GraphQL error (${response.status}): ${text}`); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Queries the daemon's pooled commands and returns true when the given | ||
| * transaction ID is currently staged for inclusion in a block. | ||
| */ | ||
| async checkTransactionInPool(transactionId) { | ||
| const query = ` | ||
| query MyQuery { | ||
| pooledUserCommands { | ||
| id | ||
| } | ||
| } | ||
| `; | ||
|
|
||
| try { | ||
| const response = await fetch(this.url, { | ||
| method: 'POST', | ||
| headers: { 'Content-Type': 'application/json' }, | ||
| body: JSON.stringify({ | ||
| operationName: 'MyQuery', | ||
| query, | ||
| variables: {} | ||
| }), | ||
| }); | ||
|
|
||
| const rawBody = await response.text(); | ||
| if (response.status !== 200) { | ||
| throw new Error(`GraphQL error (${response.status}): ${rawBody}`); | ||
| } | ||
|
|
||
| let json; | ||
| try { | ||
| json = JSON.parse(rawBody); | ||
| } catch (parseError) { | ||
| throw new Error( | ||
| `Unexpected JSON payload when checking pool: ${parseError.message}. Raw response: ${rawBody}` | ||
| ); | ||
| } | ||
|
|
||
| if (json.errors?.length) { | ||
| const combinedErrors = json.errors | ||
| .map(error => error.message ?? JSON.stringify(error)) | ||
| .join(' | '); | ||
| throw new Error(`GraphQL errors while checking pool: ${combinedErrors}`); | ||
| } | ||
|
|
||
| const pooledCommands = json.data?.pooledUserCommands || []; | ||
| return pooledCommands.some(command => command.id === transactionId); | ||
| } catch (error) { | ||
| console.error('Error checking transaction in pool:', error.message); | ||
| throw error; | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Convenience method that lists transaction IDs in the current pool. | ||
| * Useful for manual debugging or exploratory scripts. | ||
| */ | ||
| async getPooledUserCommands() { | ||
| const query = ` | ||
| query MyQuery { | ||
| pooledUserCommands { | ||
| id | ||
| } | ||
| } | ||
| `; | ||
|
|
||
| try { | ||
| const response = await fetch(this.url, { | ||
| method: 'POST', | ||
| headers: { 'Content-Type': 'application/json' }, | ||
| body: JSON.stringify({ | ||
| operationName: 'MyQuery', | ||
| query, | ||
| variables: {} | ||
| }), | ||
| }); | ||
|
|
||
| const rawBody = await response.text(); | ||
| if (response.status !== 200) { | ||
| throw new Error(`GraphQL error (${response.status}): ${rawBody}`); | ||
| } | ||
|
|
||
| let json; | ||
| try { | ||
| json = JSON.parse(rawBody); | ||
| } catch (parseError) { | ||
| throw new Error( | ||
| `Unexpected JSON payload when fetching pooled commands: ${parseError.message}. Raw response: ${rawBody}` | ||
| ); | ||
| } | ||
|
|
||
| if (json.errors?.length) { | ||
| const combinedErrors = json.errors | ||
| .map(error => error.message ?? JSON.stringify(error)) | ||
| .join(' | '); | ||
| throw new Error(`GraphQL errors while fetching pooled commands: ${combinedErrors}`); | ||
| } | ||
|
|
||
| console.log('📦 Pooled commands response payload:'); | ||
| console.dir(json, { depth: null }); | ||
| return json.data?.pooledUserCommands || []; | ||
| } catch (error) { | ||
| console.error('Error fetching pooled commands:', error.message); | ||
| throw error; | ||
| } | ||
| } | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i hope this private key is random x)