Skip to content

Commit 939a748

Browse files
committed
update after testing verification for both go and ts
1 parent 57caa93 commit 939a748

7 files changed

Lines changed: 1149 additions & 280 deletions

File tree

src/content/cre/guides/workflow/using-http-client/index.mdx

Lines changed: 11 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -20,32 +20,23 @@ The CRE SDK provides an HTTP client that allows your workflows to interact with
2020

2121
{/* prettier-ignore */}
2222
<Aside type="caution" title="Parse responses before aggregation">
23-
When using a numeric aggregation method (such as `median`), always parse the HTTP response **inside** your node function and return a numeric value never pass the raw response body to the aggregation step. If your endpoint returns an error string and your node function passes that string to a median aggregation, consensus will fail with `unsupported type for median aggregation`. See the [best practices section](/cre/guides/workflow/using-http-client/get-request#best-practices) of the GET request guide for correct and incorrect patterns.
23+
When using a numeric aggregation method (such as `median`), always parse the HTTP response **inside** your node function and return a numeric value: never pass the raw response body to the aggregation step. If your endpoint returns an error string and your node function passes that string to a median aggregation, consensus will fail with `unsupported type for median aggregation`. See the [best practices section](/cre/guides/workflow/using-http-client/get-request#best-practices) of the GET request guide for correct and incorrect patterns.
2424
</Aside>
2525

26-
These guides will walk you through the common use cases for the HTTP client.
26+
## Guides
27+
28+
- **[Making GET Requests](/cre/guides/workflow/using-http-client/get-request)**: Learn how to fetch data from a public API using a `GET` request.
29+
- **[Making POST Requests](/cre/guides/workflow/using-http-client/post-request)**: Learn how to send data to an external endpoint using a `POST` request.
30+
- **[Submitting Reports via HTTP](/cre/guides/workflow/using-http-client/submitting-reports-http)**: Learn how to submit cryptographically signed reports to an external HTTP endpoint.
31+
- **[Verifying CRE Reports Offchain](/cre/guides/workflow/using-http-client/verifying-reports-offchain)**: Verify report signatures and read workflow metadata when receiving reports over HTTP or other offchain channels.
2732

2833
## CRE reports over HTTP
2934

3035
A **CRE report** is a DON-signed package your workflow creates with `runtime.report()` (TypeScript) or `runtime.GenerateReport()` (Go). It contains your encoded payload, workflow metadata, and cryptographic signatures.
3136

32-
Most secure HTTP integrations involve two parties:
33-
34-
**Sender workflow (CRE):** runs on a schedule or trigger, does your logic, then creates and ships the report:
35-
36-
1. Run business logic and encode your payload.
37-
2. Call `runtime.report()` / `GenerateReport()` so the DON signs the report.
38-
3. Call `sendReport()` to POST the report to your URL.
37+
A typical secure integration uses two parties:
3938

40-
**Receiver:** a separate system that gets that HTTP POST (your API, or another CRE workflow with an HTTP trigger):
39+
1. **Sender:** a CRE workflow that runs your logic, signs a report, and POSTs it to a URL. See [Submitting Reports via HTTP](/cre/guides/workflow/using-http-client/submitting-reports-http).
40+
2. **Receiver:** your API or another CRE workflow that verifies the report before using the data. See [Verifying CRE Reports Offchain](/cre/guides/workflow/using-http-client/verifying-reports-offchain).
4141

42-
4. Verify signatures with `Report.parse()` / `ParseReport()`, then use the payload. See [Verifying CRE Reports Offchain](/cre/guides/workflow/using-http-client/verifying-reports-offchain).
43-
44-
The sender **creates** the report in step 2; you do not fetch it from somewhere else first. The receiver **must** verify before trusting the data: the sender does not do that for you.
45-
46-
## Guides
47-
48-
- **[Making GET Requests](/cre/guides/workflow/using-http-client/get-request)**: Learn how to fetch data from a public API using a `GET` request.
49-
- **[Making POST Requests](/cre/guides/workflow/using-http-client/post-request)**: Learn how to send data to an external endpoint using a `POST` request.
50-
- **[Submitting Reports via HTTP](/cre/guides/workflow/using-http-client/submitting-reports-http)**: Learn how to submit cryptographically signed reports to an external HTTP endpoint.
51-
- **[Verifying CRE Reports Offchain](/cre/guides/workflow/using-http-client/verifying-reports-offchain)**: Verify report signatures and read workflow metadata when receiving reports over HTTP or other offchain channels.
42+
The sender creates the report inside the workflow; the receiver must verify signatures before trusting the payload.

src/content/cre/guides/workflow/using-http-client/submitting-reports-http-go.mdx

Lines changed: 104 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,6 @@ This guide is for the **sender** side: a CRE workflow that **creates** a signed
4141
3. `runtime.GenerateReport()`: DON produces a signed `sdk.ReportResponse`.
4242
4. `SendReport()`: format and POST to your URL.
4343

44-
For a conceptual overview, see [API Interactions: CRE reports over HTTP](/cre/guides/workflow/using-http-client#cre-reports-over-http).
45-
4644
## Prerequisites
4745

4846
- Familiarity with [making POST requests](/cre/guides/workflow/using-http-client/post-request)
@@ -54,9 +52,21 @@ For a conceptual overview, see [API Interactions: CRE reports over HTTP](/cre/gu
5452
HTTP requests to URLs that return redirects (3xx status codes) will fail. Ensure the URL you provide is the final destination and does not redirect to another URL.
5553
</Aside>
5654

57-
## Quick start: Minimal example
55+
## Payload contract (if you verify offchain)
56+
57+
Receivers using [Verifying CRE Reports Offchain](/cre/guides/workflow/using-http-client/verifying-reports-offchain-go) expect JSON with hex fields **without** `0x`. Use JSON key `context` for `reportContext`:
58+
59+
| JSON field | SDK field on `ReportResponse` |
60+
| ------------ | ----------------------------- |
61+
| `report` | `RawReport` |
62+
| `context` | `ReportContext` |
63+
| `signatures` | per-node signatures in `Sigs` |
64+
65+
## Quick start: Minimal example (binary only)
5866

59-
Here's the simplest possible workflow that generates and submits a report via HTTP:
67+
Posts **raw `RawReport` bytes** only. Not compatible with the verify guide’s JSON receiver. For local testing, use the [complete working example](#complete-working-example) or [Pattern 4 for offchain verification (hex)](#pattern-4-for-offchain-verification-hex).
68+
69+
Here's the simplest workflow that generates and submits a report via HTTP:
6070

6171
```go
6272
func formatReportSimple(r *sdk.ReportResponse) (*http.Request, error) {
@@ -293,6 +303,51 @@ func formatReportAsJSON(r *sdk.ReportResponse) (*http.Request, error) {
293303
}
294304
```
295305

306+
### Pattern 4 for offchain verification (hex)
307+
308+
Use when testing [Verifying CRE Reports Offchain](/cre/guides/workflow/using-http-client/verifying-reports-offchain-go). The verify examples decode **hex without `0x`** in JSON. Base64 Pattern 4 above does not match unless you change the receiver.
309+
310+
```go
311+
import (
312+
"encoding/hex"
313+
"encoding/json"
314+
)
315+
316+
type ReportPayload struct {
317+
Report string `json:"report"`
318+
Context string `json:"context"`
319+
Signatures []string `json:"signatures"`
320+
}
321+
322+
func formatReportAsJSONHex(config *Config) func(r *sdk.ReportResponse) (*http.Request, error) {
323+
return func(r *sdk.ReportResponse) (*http.Request, error) {
324+
sigs := make([]string, len(r.Sigs))
325+
for i, sig := range r.Sigs {
326+
sigs[i] = hex.EncodeToString(sig.Signature)
327+
}
328+
payload := ReportPayload{
329+
Report: hex.EncodeToString(r.RawReport),
330+
Context: hex.EncodeToString(r.ReportContext),
331+
Signatures: sigs,
332+
}
333+
body, err := json.Marshal(payload)
334+
if err != nil {
335+
return nil, err
336+
}
337+
return &http.Request{
338+
Url: config.ApiUrl,
339+
Method: "POST",
340+
Body: body,
341+
Headers: map[string]string{"Content-Type": "application/json"},
342+
CacheSettings: &http.CacheSettings{
343+
Store: true,
344+
MaxAge: durationpb.New(60 * time.Second),
345+
},
346+
}, nil
347+
}
348+
}
349+
```
350+
296351
### Understanding `CacheSettings` for reports
297352

298353
You'll notice that all the patterns above include `CacheSettings`. This is critical for report submissions, just like it is for [POST requests](/cre/guides/workflow/using-http-client/post-request).
@@ -368,19 +423,20 @@ func onCronTrigger(config *Config, runtime cre.Runtime, trigger *cron.Payload) (
368423
This example shows a workflow that:
369424

370425
1. Generates a report from a single value
371-
1. Submits it to an HTTP API
372-
1. Uses the simple "report in body" format
426+
1. Submits it via **Pattern 4 JSON with hex fields** (compatible with the verify guide sim loop)
427+
1. Uses `config.ApiUrl` from your target config
373428

374429
```go
375430
//go:build wasip1
376431

377432
package main
378433

379434
import (
435+
"encoding/hex"
436+
"encoding/json"
380437
"fmt"
381438
"log/slog"
382439
"math/big"
383-
"time"
384440

385441
"github.com/ethereum/go-ethereum/accounts/abi"
386442
"github.com/smartcontractkit/chainlink-protos/cre/go/sdk"
@@ -408,28 +464,47 @@ func InitWorkflow(config *Config, logger *slog.Logger, secretsProvider cre.Secre
408464
}, nil
409465
}
410466

411-
// Transformation function: defines how the API expects the report
412-
func formatReportForMyAPI(r *sdk.ReportResponse) (*http.Request, error) {
413-
return &http.Request{
414-
Url: "https://webhook.site/your-unique-id", // Replace with your API
415-
Method: "POST",
416-
Body: r.RawReport,
417-
Headers: map[string]string{
418-
"Content-Type": "application/octet-stream",
419-
"X-Report-SeqNr": fmt.Sprintf("%d", r.SeqNr),
420-
},
421-
CacheSettings: &http.CacheSettings{
422-
Store: true, // Prevent duplicate submissions
423-
MaxAge: durationpb.New(60 * time.Second), // Accept cached responses up to 60 seconds old
424-
},
425-
}, nil
467+
type reportPayload struct {
468+
Report string `json:"report"`
469+
Context string `json:"context"`
470+
Signatures []string `json:"signatures"`
471+
}
472+
473+
func formatReportForMyAPI(config *Config) func(r *sdk.ReportResponse) (*http.Request, error) {
474+
return func(r *sdk.ReportResponse) (*http.Request, error) {
475+
sigs := make([]string, len(r.Sigs))
476+
for i, sig := range r.Sigs {
477+
sigs[i] = hex.EncodeToString(sig.Signature)
478+
}
479+
body, err := json.Marshal(reportPayload{
480+
Report: hex.EncodeToString(r.RawReport),
481+
Context: hex.EncodeToString(r.ReportContext),
482+
Signatures: sigs,
483+
})
484+
if err != nil {
485+
return nil, err
486+
}
487+
return &http.Request{
488+
Url: config.ApiUrl,
489+
Method: "POST",
490+
Body: body,
491+
Headers: map[string]string{
492+
"Content-Type": "application/json",
493+
"X-Report-SeqNr": fmt.Sprintf("%d", r.SeqNr),
494+
},
495+
CacheSettings: &http.CacheSettings{
496+
Store: true,
497+
MaxAge: durationpb.New(60 * time.Second),
498+
},
499+
}, nil
500+
}
426501
}
427502

428503
// Function that submits the report via HTTP
429504
func submitReportViaHTTP(config *Config, logger *slog.Logger, sendRequester *http.SendRequester, report *cre.Report) (*SubmitResponse, error) {
430505
logger.Info("Submitting report to API", "url", config.ApiUrl)
431506

432-
resp, err := sendRequester.SendReport(*report, formatReportForMyAPI).Await()
507+
resp, err := sendRequester.SendReport(*report, formatReportForMyAPI(config)).Await()
433508
if err != nil {
434509
return nil, fmt.Errorf("failed to send report: %w", err)
435510
}
@@ -521,7 +596,11 @@ func main() {
521596
```bash
522597
cre workflow simulate my-workflow --target staging-settings
523598
```
524-
1. Check webhook.site to see the report data received
599+
1. On webhook.site, open **Content** and confirm JSON with `report`, `context`, and `signatures` (hex). Use that JSON to test a receiver workflow in [Verifying CRE Reports Offchain](/cre/guides/workflow/using-http-client/verifying-reports-offchain-go#testing-locally-with-simulation).
600+
601+
## Next step: verify on the receiver
602+
603+
The sender does not validate the report for the receiver. After submission, the ingesting side must verify signatures before trusting the payload. See [Verifying CRE Reports Offchain](/cre/guides/workflow/using-http-client/verifying-reports-offchain-go).
525604

526605
## Advanced: Low-level pattern
527606

@@ -588,8 +667,7 @@ func onCronTrigger(config *Config, runtime cre.Runtime, trigger *cron.Payload) (
588667

589668
## Learn more
590669

591-
- **[API Interactions: CRE reports over HTTP](/cre/guides/workflow/using-http-client#cre-reports-over-http):** sender → receiver overview
592-
- **[Verifying CRE Reports Offchain](/cre/guides/workflow/using-http-client/verifying-reports-offchain-go):** receiver workflow; verify before trusting payload
670+
- **[Verifying CRE Reports Offchain](/cre/guides/workflow/using-http-client/verifying-reports-offchain-go):** verify signatures on the receiver before trusting payload data
593671
- **[HTTP Client SDK Reference](/cre/reference/sdk/http-client):** complete API reference including `SendReport` and `sdk.ReportResponse`
594672
- **[POST Requests](/cre/guides/workflow/using-http-client/post-request):** HTTP request patterns and caching
595673
- **[Generating Reports: Single Values](/cre/guides/workflow/using-evm-client/onchain-write/generating-reports-single-values):** create reports from single values

0 commit comments

Comments
 (0)