-
Notifications
You must be signed in to change notification settings - Fork 57
160 multi chain payment support #238
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
base: main
Are you sure you want to change the base?
Changes from all commits
0ab9123
ab377b5
ad60319
89bfe25
293ec41
9eabc47
29db11a
1d234dc
f222f9f
67e1af4
dcce2c6
05797e2
954f7d9
db96cd3
3a55052
073af1f
acb1f65
f79b8d2
54be7ba
08211cf
7c51359
b2108f6
357706a
414035b
d9726ba
79e9f7c
9a0bfb9
632dae1
9c38d69
0c73668
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -371,6 +371,12 @@ components: | |
| chainId: | ||
| type: integer | ||
| example: 84532 | ||
| supportedChainIds: | ||
|
Contributor
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.
This adds Useful? React with 👍 / 👎. |
||
| type: array | ||
| items: | ||
| type: integer | ||
| format: int64 | ||
| description: List of supported blockchain network chain IDs for this payment challenge context. | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| timestamp: | ||
| type: integer | ||
| format: uint64 | ||
|
|
@@ -456,7 +462,7 @@ components: | |
| format: date-time | ||
| payment: | ||
| type: object | ||
| required: [payer, recipient, amount, token, chainId, nonce] | ||
| required: [payer, recipient, amount, token, chainId, supportedChainIds, nonce] | ||
| properties: | ||
| payer: | ||
| type: string | ||
|
|
@@ -473,6 +479,12 @@ components: | |
| chainId: | ||
| type: integer | ||
| example: 84532 | ||
| supportedChainIds: | ||
| type: array | ||
| items: | ||
| type: integer | ||
| format: int64 | ||
| description: List of supported blockchain network chain IDs for this payment challenge context. | ||
| nonce: | ||
| type: string | ||
| example: "550e8400-e29b-41d4-a716-446655440000" | ||
|
|
@@ -489,3 +501,5 @@ components: | |
| response_hash: | ||
| type: string | ||
| example: sha256:... | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -24,12 +24,13 @@ type Receipt struct { | |
|
|
||
| // PaymentDetails contains payment-related information | ||
| type PaymentDetails struct { | ||
| Payer string `json:"payer"` | ||
| Recipient string `json:"recipient"` | ||
| Amount string `json:"amount"` | ||
| Token string `json:"token"` | ||
| ChainID int `json:"chainId"` | ||
| Nonce string `json:"nonce"` | ||
| Payer string `json:"payer"` | ||
| Recipient string `json:"recipient"` | ||
| Amount string `json:"amount"` | ||
| Token string `json:"token"` | ||
| ChainID int64 `json:"chainId"` | ||
| ChainIDs []int64 `json:"supportedChainIds"` // Advertises multi-chain capability | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| Nonce string `json:"nonce"` | ||
| } | ||
|
|
||
| // ServiceDetails contains service-related information | ||
|
|
@@ -53,7 +54,8 @@ type ReceiptStore interface { | |
| Close() error | ||
| } | ||
|
|
||
| // GenerateReceipt creates a new receipt for a successful payment | ||
| // GenerateReceipt creates a new receipt for a successful payment, | ||
| // wiring in the global SupportedChainIDs registry. | ||
| func GenerateReceipt(payment PaymentContext, payer string, endpoint string, reqBody, respBody []byte) (*SignedReceipt, error) { | ||
| receiptID, err := generateReceiptID() | ||
| if err != nil { | ||
|
|
@@ -69,7 +71,8 @@ func GenerateReceipt(payment PaymentContext, payer string, endpoint string, reqB | |
| Recipient: payment.Recipient, | ||
| Amount: payment.Amount, | ||
| Token: payment.Token, | ||
| ChainID: payment.ChainID, | ||
| ChainID: int64(payment.ChainID), // Fixed: Explicit int64 type cast resolves compiler type mismatch | ||
| ChainIDs: SupportedChainIDs, // Inject multi-chain registry | ||
|
Contributor
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.
This field now references Useful? React with 👍 / 👎. |
||
| Nonce: payment.Nonce, | ||
| }, | ||
| Service: ServiceDetails{ | ||
|
|
@@ -83,9 +86,7 @@ func GenerateReceipt(payment PaymentContext, payer string, endpoint string, reqB | |
| } | ||
|
|
||
| // generateReceiptID generates a unique receipt ID with "rcpt_" prefix | ||
| // Returns error if random generation fails to prevent predictable IDs | ||
| func generateReceiptID() (string, error) { | ||
| // Generate 6 random bytes (12 hex characters) | ||
| bytes := make([]byte, 6) | ||
| if _, err := rand.Read(bytes); err != nil { | ||
| return "", fmt.Errorf("failed to generate random receipt ID: %w", err) | ||
|
|
@@ -96,42 +97,30 @@ func generateReceiptID() (string, error) { | |
| // hashData computes SHA-256 hash of data and returns hex-encoded string | ||
| func hashData(data []byte) string { | ||
| if len(data) == 0 { | ||
| return "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" // Empty hash | ||
| return "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" | ||
| } | ||
| hash := sha256.Sum256(data) | ||
| return "sha256:" + hex.EncodeToString(hash[:]) | ||
| } | ||
|
|
||
| // signReceipt signs a receipt using the server's private key | ||
| // NOTE: Go's json.Marshal is deterministic for structs - fields are always | ||
| // serialized in the order they are defined in the struct, ensuring consistent output. | ||
| // This guarantees consistent signatures across multiple marshaling operations. | ||
| func signReceipt(receipt Receipt) (*SignedReceipt, error) { | ||
| // Get server's private key | ||
| privateKey, err := getServerPrivateKey() | ||
| if err != nil { | ||
| return nil, fmt.Errorf("failed to load server private key: %w", err) | ||
| } | ||
|
|
||
| // Serialize receipt deterministically | ||
| // json.Marshal outputs struct fields in their declaration order | ||
| receiptBytes, err := json.Marshal(receipt) | ||
| if err != nil { | ||
| return nil, fmt.Errorf("failed to marshal receipt: %w", err) | ||
| } | ||
|
|
||
| // Hash the receipt using Keccak256 (Ethereum-compatible) | ||
| hash := crypto.Keccak256Hash(receiptBytes) | ||
|
|
||
| // Sign the hash using ECDSA | ||
| // SECURITY: crypto.Sign uses constant-time operations from go-ethereum's secp256k1 implementation | ||
| // This prevents timing attacks that could leak private key information | ||
| signature, err := crypto.Sign(hash.Bytes(), privateKey) | ||
| if err != nil { | ||
| return nil, fmt.Errorf("failed to sign receipt: %w", err) | ||
| } | ||
|
|
||
| // Get server's public key for verification | ||
| publicKey := privateKey.Public().(*ecdsa.PublicKey) | ||
| publicKeyBytes := crypto.FromECDSAPub(publicKey) | ||
|
|
||
|
|
||
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.
This gateway allowlist advertises
11155240as Optimism Sepolia, but the verifier'sSUPPORTED_CHAINSand the web wallet metadata use11155420. Once the supported-chain helper is wired into challenges or receipts, clients can be told to use a chain the verifier/web do not support, so Optimism Sepolia payments will fail unless this ID is made consistent.Useful? React with 👍 / 👎.