trac-peer 0.4.4 → 0.4.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/APP_DEV.md +38 -28
- package/DOCS.md +8 -6
- package/PEER_RPC.md +189 -0
- package/package.json +1 -1
- package/rpc/handlers.js +3 -9
- package/rpc/routes/v1.js +2 -3
- package/rpc/services.js +28 -23
- package/tests/acceptance/rpc.test.js +24 -8
package/APP_DEV.md
CHANGED
|
@@ -118,7 +118,7 @@ Endpoints (all JSON, all under `/v1`):
|
|
|
118
118
|
- `GET /v1/status`
|
|
119
119
|
- `GET /v1/contract/schema`
|
|
120
120
|
- `GET /v1/contract/nonce`
|
|
121
|
-
- `
|
|
121
|
+
- `GET /v1/contract/tx/context` (returns MSB tx context)
|
|
122
122
|
- `POST /v1/contract/tx`
|
|
123
123
|
- `GET /v1/state?key=<urlencoded>&confirmed=true|false`
|
|
124
124
|
|
|
@@ -128,19 +128,20 @@ Important notes:
|
|
|
128
128
|
|
|
129
129
|
---
|
|
130
130
|
|
|
131
|
-
## 6)
|
|
131
|
+
## 6) Client → peer → contract flow (end-to-end)
|
|
132
132
|
|
|
133
|
-
This is the “Ethereum-style” flow:
|
|
133
|
+
This is the “Ethereum-style” flow: a client (typically a dapp/backend) discovers a peer URL, fetches a schema, prepares a tx, requests a wallet signature, then submits it.
|
|
134
134
|
|
|
135
|
-
### Where the dapp fits
|
|
135
|
+
### Where the dapp fits (dapp constructs, wallet signs)
|
|
136
136
|
|
|
137
|
-
- A **dapp** (web/mobile UI)
|
|
138
|
-
- For writes
|
|
139
|
-
1)
|
|
140
|
-
2)
|
|
141
|
-
3)
|
|
137
|
+
- A **dapp** (web/mobile UI) can read: `GET /v1/contract/schema` and `GET /v1/state`.
|
|
138
|
+
- For **writes**, the dapp (or a backend the dapp calls) typically:
|
|
139
|
+
1) fetches `nonce` + `tx/context` from the peer,
|
|
140
|
+
2) constructs the tx hash (`tx`) locally,
|
|
141
|
+
3) asks the wallet to **sign** the tx hash,
|
|
142
|
+
4) submits `sim: true` then `sim: false` to the peer.
|
|
142
143
|
|
|
143
|
-
In other words: the
|
|
144
|
+
In other words: the wallet only needs to sign; it does not need to talk to the peer RPC.
|
|
144
145
|
|
|
145
146
|
### Step A — Discover contract schema
|
|
146
147
|
|
|
@@ -148,42 +149,44 @@ In other words: the dapp never needs the private key; it just passes data betwee
|
|
|
148
149
|
curl -s http://127.0.0.1:5001/v1/contract/schema | jq
|
|
149
150
|
```
|
|
150
151
|
|
|
151
|
-
|
|
152
|
+
Client uses:
|
|
152
153
|
- `contract.txTypes` (what tx types exist)
|
|
153
154
|
- `contract.ops[type]` (input structure for each type, when available)
|
|
154
155
|
- `api.methods` (optional read/query methods exposed by the protocol api)
|
|
155
156
|
|
|
156
|
-
### Step B — Get a nonce
|
|
157
|
+
### Step B — Get a nonce (client)
|
|
157
158
|
|
|
158
159
|
```sh
|
|
159
160
|
curl -s http://127.0.0.1:5001/v1/contract/nonce | jq
|
|
160
161
|
```
|
|
161
162
|
|
|
162
|
-
### Step C —
|
|
163
|
+
### Step C — Get tx context + build tx hash (client)
|
|
163
164
|
|
|
164
|
-
The
|
|
165
|
+
The client constructs a typed command (this is app-specific):
|
|
165
166
|
|
|
166
167
|
```json
|
|
167
168
|
{ "type": "catch", "value": {} }
|
|
168
169
|
```
|
|
169
170
|
|
|
170
|
-
Then
|
|
171
|
+
Then the client asks the peer for the MSB tx context (no computation):
|
|
171
172
|
|
|
172
173
|
```sh
|
|
173
|
-
curl -s
|
|
174
|
-
-H 'Content-Type: application/json' \
|
|
175
|
-
-d '{
|
|
176
|
-
"prepared_command": { "type": "catch", "value": {} },
|
|
177
|
-
"address": "<wallet-pubkey-hex32>",
|
|
178
|
-
"nonce": "<nonce-hex32>"
|
|
179
|
-
}' | jq
|
|
174
|
+
curl -s http://127.0.0.1:5001/v1/contract/tx/context | jq
|
|
180
175
|
```
|
|
181
176
|
|
|
182
|
-
The response contains:
|
|
183
|
-
- `
|
|
184
|
-
- `
|
|
177
|
+
The response contains an `msb` object with the fields the client needs to build the tx preimage:
|
|
178
|
+
- `networkId`
|
|
179
|
+
- `txv`
|
|
180
|
+
- `iw` (peer writer key)
|
|
181
|
+
- `bs` (subnet bootstrap)
|
|
182
|
+
- `mbs` (MSB bootstrap)
|
|
183
|
+
- `operationType` (currently `12`)
|
|
184
|
+
|
|
185
|
+
From there, the client computes locally:
|
|
186
|
+
- `command_hash = blake3(JSON.stringify(prepared_command))` (hex32)
|
|
187
|
+
- `tx = blake3(createMessage(networkId, txv, iw, command_hash, bs, mbs, nonce, operationType))` (hex32)
|
|
185
188
|
|
|
186
|
-
### Step D — Sign locally
|
|
189
|
+
### Step D — Sign locally with the wallet
|
|
187
190
|
|
|
188
191
|
Wallet signs the **bytes** of `tx` (32 bytes) with its private key to produce:
|
|
189
192
|
- `signature` (hex64)
|
|
@@ -222,10 +225,17 @@ curl -s -X POST http://127.0.0.1:5001/v1/contract/tx \
|
|
|
222
225
|
|
|
223
226
|
### Step G — Read app state
|
|
224
227
|
|
|
225
|
-
Apps typically write under `app
|
|
228
|
+
Apps typically write under `app/...` (app-defined). Read via:
|
|
226
229
|
|
|
227
230
|
```sh
|
|
228
|
-
curl -s 'http://127.0.0.1:5001/v1/state?key
|
|
231
|
+
curl -s 'http://127.0.0.1:5001/v1/state?key=<urlencoded-hyperbee-key>&confirmed=false' | jq
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
Example (Tuxemon demo app):
|
|
235
|
+
|
|
236
|
+
```sh
|
|
237
|
+
curl -s 'http://127.0.0.1:5001/v1/state?key=app%2Ftuxedex%2F<wallet-pubkey-hex32>&confirmed=false' | jq
|
|
238
|
+
```
|
|
229
239
|
```
|
|
230
240
|
|
|
231
241
|
The `confirmed` flag controls whether you read from:
|
package/DOCS.md
CHANGED
|
@@ -418,9 +418,9 @@ npm run peer:pear-rpc -- \
|
|
|
418
418
|
- `GET /v1/contract/schema`
|
|
419
419
|
- Read state:
|
|
420
420
|
- `GET /v1/state?key=app%2Fkv%2Ffoo&confirmed=true`
|
|
421
|
-
-
|
|
421
|
+
- Client tx flow (dapp constructs, wallet signs):
|
|
422
422
|
- `GET /v1/contract/nonce`
|
|
423
|
-
- `
|
|
423
|
+
- `GET /v1/contract/tx/context` (returns MSB tx context for client-side tx derivation)
|
|
424
424
|
- `POST /v1/contract/tx` body: `{ "tx": "<hex32>", "prepared_command": { ... }, "address": "<pubkey-hex32>", "signature": "<hex64>", "nonce": "<hex32>", "sim": true|false }`
|
|
425
425
|
|
|
426
426
|
Notes:
|
|
@@ -446,7 +446,7 @@ All nodes in the subnet must run the same Protocol/Contract logic for determinis
|
|
|
446
446
|
|
|
447
447
|
## How `/tx` works (the lifecycle)
|
|
448
448
|
|
|
449
|
-
When you run `/tx --command "..."` in the CLI (or a
|
|
449
|
+
When you run `/tx --command "..."` in the CLI (or a client uses the RPC tx flow), the flow is:
|
|
450
450
|
|
|
451
451
|
1) The command string is mapped into an operation object: `{ type, value }`.
|
|
452
452
|
2) trac-peer hashes and signs the operation and broadcasts a settlement tx to MSB.
|
|
@@ -458,9 +458,11 @@ Where does step (1) happen?
|
|
|
458
458
|
- In the demo runner (`scripts/run-peer.mjs`) it’s in the protocol class’s `mapTxCommand(...)` (example: `src/dev/tuxemonProtocol.js`).
|
|
459
459
|
- The base protocol method is `Protocol.mapTxCommand(...)` in `src/protocol.js`. For your own app you override that function.
|
|
460
460
|
|
|
461
|
-
|
|
462
|
-
- The
|
|
463
|
-
- The
|
|
461
|
+
Client tx flow specifics:
|
|
462
|
+
- The client fetches MSB tx context from `GET /v1/contract/tx/context`.
|
|
463
|
+
- The client computes `command_hash = blake3(JSON.stringify(prepared_command))`, then computes `tx` from the MSB preimage fields + `nonce`.
|
|
464
|
+
- The wallet signs `tx`.
|
|
465
|
+
- The client submits the signed payload to `POST /v1/contract/tx` with `sim: true` to simulate (recommended), then `sim: false` to broadcast.
|
|
464
466
|
|
|
465
467
|
---
|
|
466
468
|
|
package/PEER_RPC.md
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
# trac-peer RPC (HTTP) — API Reference
|
|
2
|
+
|
|
3
|
+
This is a **reference** for the public HTTP RPC exposed by `trac-peer`.
|
|
4
|
+
|
|
5
|
+
Base URL example:
|
|
6
|
+
- `http://127.0.0.1:5001`
|
|
7
|
+
|
|
8
|
+
All endpoints below are under the `/v1` prefix.
|
|
9
|
+
|
|
10
|
+
## Conventions
|
|
11
|
+
|
|
12
|
+
- All responses are JSON.
|
|
13
|
+
- Request bodies (where applicable) are JSON.
|
|
14
|
+
- Hex formats:
|
|
15
|
+
- `hex32`: 32-byte hex string (64 hex chars)
|
|
16
|
+
- `hex64`: 64-byte hex string (128 hex chars)
|
|
17
|
+
|
|
18
|
+
## Errors
|
|
19
|
+
|
|
20
|
+
Error responses have the shape:
|
|
21
|
+
|
|
22
|
+
```json
|
|
23
|
+
{ "error": "message" }
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Common status codes:
|
|
27
|
+
- `200` success
|
|
28
|
+
- `400` bad request (missing/invalid parameters)
|
|
29
|
+
- `404` not found (unknown route)
|
|
30
|
+
- `413` request body too large
|
|
31
|
+
- `500` internal error
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## `GET /v1/health`
|
|
36
|
+
|
|
37
|
+
Health check.
|
|
38
|
+
|
|
39
|
+
### Response `200`
|
|
40
|
+
|
|
41
|
+
```json
|
|
42
|
+
{ "ok": true }
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## `GET /v1/status`
|
|
48
|
+
|
|
49
|
+
Returns a status summary for the running peer and its MSB client view.
|
|
50
|
+
|
|
51
|
+
### Query parameters
|
|
52
|
+
None
|
|
53
|
+
|
|
54
|
+
### Response `200`
|
|
55
|
+
|
|
56
|
+
Object with:
|
|
57
|
+
- `peer`: identifiers + subnet view info (writability, signed length, bootstrap, etc.)
|
|
58
|
+
- `msb`: MSB bootstrap/networkId/signedLength as seen by this peer’s MSB client
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## `GET /v1/contract/schema`
|
|
63
|
+
|
|
64
|
+
Returns an ABI-like schema describing:
|
|
65
|
+
- which contract tx types exist (`contract.txTypes`)
|
|
66
|
+
- optional per-tx input structure (`contract.ops`)
|
|
67
|
+
- the Protocol API method schema (`api.methods`)
|
|
68
|
+
|
|
69
|
+
### Query parameters
|
|
70
|
+
None
|
|
71
|
+
|
|
72
|
+
### Response `200`
|
|
73
|
+
|
|
74
|
+
```json
|
|
75
|
+
{
|
|
76
|
+
"schemaVersion": 1,
|
|
77
|
+
"schemaFormat": "json-schema",
|
|
78
|
+
"contract": {
|
|
79
|
+
"contractClass": "TuxemonContract",
|
|
80
|
+
"protocolClass": "TuxemonProtocol",
|
|
81
|
+
"txTypes": ["catch"],
|
|
82
|
+
"ops": {
|
|
83
|
+
"catch": { "value": {} }
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
"api": { "methods": {} }
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## `GET /v1/contract/nonce`
|
|
93
|
+
|
|
94
|
+
Generates a nonce for signing.
|
|
95
|
+
|
|
96
|
+
### Query parameters
|
|
97
|
+
None
|
|
98
|
+
|
|
99
|
+
### Response `200`
|
|
100
|
+
|
|
101
|
+
```json
|
|
102
|
+
{ "nonce": "<hex32>" }
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## `GET /v1/contract/tx/context`
|
|
108
|
+
|
|
109
|
+
Returns the MSB transaction context needed by a client/dapp to compute the `tx` hash locally.
|
|
110
|
+
|
|
111
|
+
### Query parameters
|
|
112
|
+
None
|
|
113
|
+
|
|
114
|
+
### Response `200`
|
|
115
|
+
|
|
116
|
+
```json
|
|
117
|
+
{
|
|
118
|
+
"msb": {
|
|
119
|
+
"networkId": 918,
|
|
120
|
+
"txv": "<hex32>",
|
|
121
|
+
"iw": "<hex32>",
|
|
122
|
+
"bs": "<hex32>",
|
|
123
|
+
"mbs": "<hex32>",
|
|
124
|
+
"operationType": 12
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## `POST /v1/contract/tx`
|
|
132
|
+
|
|
133
|
+
Simulates or broadcasts a signed contract transaction.
|
|
134
|
+
|
|
135
|
+
### Request body (JSON)
|
|
136
|
+
|
|
137
|
+
Required fields:
|
|
138
|
+
- `tx` (`hex32`): transaction hash computed by the client/dapp
|
|
139
|
+
- `prepared_command` (`object`): `{ "type": "<string>", "value": <any> }`
|
|
140
|
+
- `address` (`hex32`): wallet public key (hex) used for signature verification
|
|
141
|
+
- `signature` (`hex64`): ed25519 signature over `tx` bytes
|
|
142
|
+
- `nonce` (`hex32`)
|
|
143
|
+
|
|
144
|
+
Optional:
|
|
145
|
+
- `sim` (`boolean`, default `false`): when `true`, run MSB preflight + contract simulation; when `false`, broadcast
|
|
146
|
+
|
|
147
|
+
Example:
|
|
148
|
+
|
|
149
|
+
```json
|
|
150
|
+
{
|
|
151
|
+
"tx": "<hex32>",
|
|
152
|
+
"prepared_command": { "type": "catch", "value": {} },
|
|
153
|
+
"address": "<hex32>",
|
|
154
|
+
"signature": "<hex64>",
|
|
155
|
+
"nonce": "<hex32>",
|
|
156
|
+
"sim": true
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### Response `200`
|
|
161
|
+
|
|
162
|
+
```json
|
|
163
|
+
{ "result": {} }
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Result shape is protocol-dependent.
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## `GET /v1/state`
|
|
171
|
+
|
|
172
|
+
Reads a single key from the subnet state (Hyperbee).
|
|
173
|
+
|
|
174
|
+
### Query parameters
|
|
175
|
+
|
|
176
|
+
- `key` (required, string): the exact Hyperbee key to read
|
|
177
|
+
- `confirmed` (optional, boolean, default `true`):
|
|
178
|
+
- `true`: read from signed/confirmed view
|
|
179
|
+
- `false`: read from latest local view
|
|
180
|
+
|
|
181
|
+
### Response `200`
|
|
182
|
+
|
|
183
|
+
```json
|
|
184
|
+
{
|
|
185
|
+
"key": "app/tuxedex/<pubKeyHex>",
|
|
186
|
+
"confirmed": false,
|
|
187
|
+
"value": {}
|
|
188
|
+
}
|
|
189
|
+
```
|
package/package.json
CHANGED
package/rpc/handlers.js
CHANGED
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
getState,
|
|
6
6
|
getContractSchema,
|
|
7
7
|
contractGenerateNonce,
|
|
8
|
-
|
|
8
|
+
contractTxContext,
|
|
9
9
|
contractTx,
|
|
10
10
|
} from "./services.js";
|
|
11
11
|
|
|
@@ -36,14 +36,8 @@ export async function handleContractNonce({ respond, peer }) {
|
|
|
36
36
|
respond(200, { nonce });
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
export async function
|
|
40
|
-
const
|
|
41
|
-
if (!body || typeof body !== "object") return respond(400, { error: "Missing JSON body." });
|
|
42
|
-
const payload = await contractPrepareTx(peer, {
|
|
43
|
-
prepared_command: body.prepared_command,
|
|
44
|
-
address: body.address,
|
|
45
|
-
nonce: body.nonce,
|
|
46
|
-
});
|
|
39
|
+
export async function handleContractTxContext({ req, respond, peer, maxBodyBytes }) {
|
|
40
|
+
const payload = await contractTxContext(peer);
|
|
47
41
|
respond(200, payload);
|
|
48
42
|
}
|
|
49
43
|
|
package/rpc/routes/v1.js
CHANGED
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
handleGetState,
|
|
5
5
|
handleGetContractSchema,
|
|
6
6
|
handleContractNonce,
|
|
7
|
-
|
|
7
|
+
handleContractTxContext,
|
|
8
8
|
handleContractTx,
|
|
9
9
|
} from "../handlers.js";
|
|
10
10
|
|
|
@@ -13,8 +13,7 @@ export const v1Routes = [
|
|
|
13
13
|
{ method: "GET", path: "/status", handler: handleStatus },
|
|
14
14
|
{ method: "GET", path: "/state", handler: handleGetState },
|
|
15
15
|
{ method: "GET", path: "/contract/schema", handler: handleGetContractSchema },
|
|
16
|
-
// Wallet→peer flow: server-side tx prepare + wallet signature + broadcast.
|
|
17
16
|
{ method: "GET", path: "/contract/nonce", handler: handleContractNonce },
|
|
18
|
-
{ method: "
|
|
17
|
+
{ method: "GET", path: "/contract/tx/context", handler: handleContractTxContext },
|
|
19
18
|
{ method: "POST", path: "/contract/tx", handler: handleContractTx },
|
|
20
19
|
];
|
package/rpc/services.js
CHANGED
|
@@ -1,12 +1,5 @@
|
|
|
1
1
|
import b4a from "b4a";
|
|
2
2
|
import { fastestToJsonSchema } from "./utils/schemaToJson.js";
|
|
3
|
-
import { createHash } from "../src/utils/types.js";
|
|
4
|
-
|
|
5
|
-
const asHex32 = (value, field) => {
|
|
6
|
-
const hex = String(value ?? "").trim().toLowerCase();
|
|
7
|
-
if (!/^[0-9a-f]{64}$/.test(hex)) throw new Error(`Invalid ${field}. Expected 32-byte hex (64 chars).`);
|
|
8
|
-
return hex;
|
|
9
|
-
};
|
|
10
3
|
|
|
11
4
|
const isObject = (v) => v !== null && typeof v === "object" && !Array.isArray(v);
|
|
12
5
|
|
|
@@ -17,10 +10,10 @@ const requireApi = (peer) => {
|
|
|
17
10
|
};
|
|
18
11
|
|
|
19
12
|
export async function getStatus(peer) {
|
|
20
|
-
const subnetBootstrapHex = b4a.isBuffer(peer.bootstrap)
|
|
21
|
-
? b4a.toString(peer.bootstrap, "hex")
|
|
22
|
-
: peer.bootstrap != null
|
|
23
|
-
? String(peer.bootstrap)
|
|
13
|
+
const subnetBootstrapHex = b4a.isBuffer(peer.config.bootstrap)
|
|
14
|
+
? b4a.toString(peer.config.bootstrap, "hex")
|
|
15
|
+
: peer.config.bootstrap != null
|
|
16
|
+
? String(peer.config.bootstrap)
|
|
24
17
|
: null;
|
|
25
18
|
|
|
26
19
|
const peerMsbAddress = peer.msbClient.pubKeyHexToAddress(peer.wallet.publicKey);
|
|
@@ -120,22 +113,34 @@ export async function contractGenerateNonce(peer) {
|
|
|
120
113
|
return api.generateNonce();
|
|
121
114
|
}
|
|
122
115
|
|
|
123
|
-
export async function
|
|
116
|
+
export async function contractTxContext(peer) {
|
|
124
117
|
const api = requireApi(peer);
|
|
125
|
-
if (!isObject(prepared_command)) throw new Error("prepared_command must be an object.");
|
|
126
|
-
const addr = asHex32(address, "address");
|
|
127
|
-
const n = asHex32(nonce, "nonce");
|
|
128
118
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
119
|
+
const networkId = peer.msbClient.networkId;
|
|
120
|
+
const mbs = peer.msbClient.bootstrapHex;
|
|
121
|
+
const txv = await peer.msbClient.getTxvHex();
|
|
122
|
+
|
|
123
|
+
const bs =
|
|
124
|
+
b4a.isBuffer(peer.config.bootstrap)
|
|
125
|
+
? b4a.toString(peer.config.bootstrap, "hex")
|
|
126
|
+
: peer.config.bootstrap != null
|
|
127
|
+
? String(peer.config.bootstrap)
|
|
128
|
+
: null;
|
|
132
129
|
|
|
133
|
-
const
|
|
134
|
-
if (
|
|
130
|
+
const iw = peer.writerLocalKey ?? api.getPeerWriterKey?.();
|
|
131
|
+
if (!iw || !/^[0-9a-f]{64}$/i.test(String(iw))) throw new Error("Peer writer key is not available.");
|
|
132
|
+
if (!bs || !/^[0-9a-f]{64}$/i.test(String(bs))) throw new Error("Peer subnet bootstrap is not available.");
|
|
135
133
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
134
|
+
return {
|
|
135
|
+
msb: {
|
|
136
|
+
networkId,
|
|
137
|
+
txv,
|
|
138
|
+
iw: String(iw).toLowerCase(),
|
|
139
|
+
bs: String(bs).toLowerCase(),
|
|
140
|
+
mbs: String(mbs).toLowerCase(),
|
|
141
|
+
operationType: 12,
|
|
142
|
+
},
|
|
143
|
+
};
|
|
139
144
|
}
|
|
140
145
|
|
|
141
146
|
export async function contractTx(peer, { tx, prepared_command, address, signature, nonce, sim = false } = {}) {
|
|
@@ -12,6 +12,7 @@ import { Peer, Protocol, Contract, createConfig, ENV } from "../../src/index.js"
|
|
|
12
12
|
import TuxemonContract from "../../dev/tuxemonContract.js";
|
|
13
13
|
import TuxemonProtocol from "../../dev/tuxemonProtocol.js";
|
|
14
14
|
import Wallet from "../../src/wallet.js";
|
|
15
|
+
import { createHash, jsonStringify } from "../../src/utils/types.js";
|
|
15
16
|
import { mkdtempPortable, rmrfPortable } from "../helpers/tmpdir.js";
|
|
16
17
|
|
|
17
18
|
async function withTempDir(fn) {
|
|
@@ -205,10 +206,13 @@ test("rpc: body size limit returns 413", async (t) => {
|
|
|
205
206
|
const baseUrl = rpc.baseUrl;
|
|
206
207
|
|
|
207
208
|
const big = "x".repeat(100);
|
|
208
|
-
const r = await httpJson("POST", `${baseUrl}/v1/contract/tx
|
|
209
|
+
const r = await httpJson("POST", `${baseUrl}/v1/contract/tx`, {
|
|
210
|
+
tx: "0".repeat(64),
|
|
209
211
|
prepared_command: { type: big, value: {} },
|
|
210
212
|
address: wallet.publicKey,
|
|
213
|
+
signature: "0".repeat(128),
|
|
211
214
|
nonce: "0".repeat(64),
|
|
215
|
+
sim: true,
|
|
212
216
|
});
|
|
213
217
|
t.is(r.status, 413);
|
|
214
218
|
} finally {
|
|
@@ -281,13 +285,25 @@ test("rpc: wallet-signed tx simulate via prepare+sign+broadcast", async (t) => {
|
|
|
281
285
|
const nonce = nonceRes.json?.nonce;
|
|
282
286
|
|
|
283
287
|
const prepared_command = { type: "catch", value: {} };
|
|
284
|
-
const
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
t.is(
|
|
290
|
-
|
|
288
|
+
const ctx = await httpJson("GET", `${baseUrl}/v1/contract/tx/context`);
|
|
289
|
+
t.is(ctx.status, 200);
|
|
290
|
+
t.is(ctx.json?.msb?.operationType, 12);
|
|
291
|
+
t.is(typeof ctx.json?.msb?.networkId, "number");
|
|
292
|
+
t.is(typeof ctx.json?.msb?.txv, "string");
|
|
293
|
+
t.is(typeof ctx.json?.msb?.iw, "string");
|
|
294
|
+
t.is(typeof ctx.json?.msb?.bs, "string");
|
|
295
|
+
t.is(typeof ctx.json?.msb?.mbs, "string");
|
|
296
|
+
|
|
297
|
+
const command_hash = await createHash(jsonStringify(prepared_command));
|
|
298
|
+
const tx = await peer.protocol.instance.generateTx(
|
|
299
|
+
ctx.json.msb.networkId,
|
|
300
|
+
ctx.json.msb.txv,
|
|
301
|
+
ctx.json.msb.iw,
|
|
302
|
+
command_hash,
|
|
303
|
+
ctx.json.msb.bs,
|
|
304
|
+
ctx.json.msb.mbs,
|
|
305
|
+
nonce
|
|
306
|
+
);
|
|
291
307
|
|
|
292
308
|
const signature = externalWallet.sign(b4a.from(tx, "hex"));
|
|
293
309
|
const simRes = await httpJson("POST", `${baseUrl}/v1/contract/tx`, {
|