Skip to content

Commit 2c3f7de

Browse files
committed
fix(rustffi): forward the per-operation signing override to the FFI
AddDocument and its siblings accepted EnableSigning and dropped it on the floor, so a write asked to be unsigned came back signed by the node identity. The Rust side already exposes exec_request_with_signing and exec_request_in_txn_with_signing (-1 node default, 0 off, 1 on) and the vendored header declares both; the option now rides the context through coreblock.ContextWithSigning, the same pair internal/db uses, because Wrapper.ExecRequest is pinned to the client.Store signature. See defradb.rs issue 1600.
1 parent 57fefc5 commit 2c3f7de

2 files changed

Lines changed: 69 additions & 8 deletions

File tree

tests/clients/rustffi/defra.go

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,14 @@ var (
4444
initOnce sync.Once
4545
)
4646

47+
// Block signing overrides understood by the Rust FFI's
48+
// exec_request_with_signing and exec_request_in_txn_with_signing.
49+
const (
50+
signingUseNodeDefault = -1
51+
signingDisabled = 0
52+
signingEnabled = 1
53+
)
54+
4755
// mapFFIError maps raw FFI error strings to proper Go error types.
4856
//
4957
// FFI errors are flat strings like "not authorized to perform operation. Permission: xyz".
@@ -375,7 +383,7 @@ type ExecRequestResult struct {
375383
// Returns the raw JSON response string.
376384
// identityDID is the DID of the caller for ACP permission checks (empty string for anonymous).
377385
func (n *Node) ExecRequest(identityDID string, query string, operationName string, variables string) (string, error) {
378-
result, err := n.ExecRequestFull(identityDID, query, operationName, variables)
386+
result, err := n.ExecRequestFull(identityDID, query, operationName, variables, signingUseNodeDefault)
379387
if err != nil {
380388
return "", err
381389
}
@@ -387,7 +395,14 @@ func (n *Node) ExecRequest(identityDID string, query string, operationName strin
387395

388396
// ExecRequestFull executes a GraphQL query, mutation, or subscription.
389397
// Returns an ExecRequestResult that indicates whether the result is a subscription.
390-
func (n *Node) ExecRequestFull(identityDID string, query string, operationName string, variables string) (*ExecRequestResult, error) {
398+
// signingOverride is one of signingUseNodeDefault, signingDisabled or signingEnabled.
399+
func (n *Node) ExecRequestFull(
400+
identityDID string,
401+
query string,
402+
operationName string,
403+
variables string,
404+
signingOverride int,
405+
) (*ExecRequestResult, error) {
391406
var cIdentityDID *C.char
392407
if identityDID != "" {
393408
cIdentityDID = C.CString(identityDID)
@@ -412,7 +427,9 @@ func (n *Node) ExecRequestFull(identityDID string, query string, operationName s
412427
cBatchSessionID := C.CString("")
413428
defer C.free(unsafe.Pointer(cBatchSessionID))
414429

415-
result := C.exec_request(n.ptr, cIdentityDID, cQuery, cOpName, cVars, cBatchSessionID)
430+
result := C.exec_request_with_signing(
431+
n.ptr, cIdentityDID, cQuery, cOpName, cVars, cBatchSessionID, C.int(signingOverride),
432+
)
416433

417434
switch result.status {
418435
case 0: // Success - query/mutation result
@@ -597,7 +614,14 @@ func (t *Transaction) Rollback() error {
597614

598615
// ExecRequest executes a GraphQL query or mutation within the transaction.
599616
// identityDID is the DID of the caller for ACP permission checks (empty string for anonymous).
600-
func (t *Transaction) ExecRequest(identityDID string, query string, operationName string, variables string) (string, error) {
617+
// signingOverride is one of signingUseNodeDefault, signingDisabled or signingEnabled.
618+
func (t *Transaction) ExecRequest(
619+
identityDID string,
620+
query string,
621+
operationName string,
622+
variables string,
623+
signingOverride int,
624+
) (string, error) {
601625
cTxnID := C.CString(t.id)
602626
defer C.free(unsafe.Pointer(cTxnID))
603627

@@ -625,7 +649,9 @@ func (t *Transaction) ExecRequest(identityDID string, query string, operationNam
625649
cBatchSessionID := C.CString("")
626650
defer C.free(unsafe.Pointer(cBatchSessionID))
627651

628-
result := C.exec_request_in_txn(t.node.ptr, cTxnID, cIdentityDID, cQuery, cOpName, cVars, cBatchSessionID)
652+
result := C.exec_request_in_txn_with_signing(
653+
t.node.ptr, cTxnID, cIdentityDID, cQuery, cOpName, cVars, cBatchSessionID, C.int(signingOverride),
654+
)
629655

630656
if result.status != 0 {
631657
err := C.GoString(result.error)
@@ -640,7 +666,7 @@ func (t *Transaction) ExecRequest(identityDID string, query string, operationNam
640666

641667
// Query executes a GraphQL query within the transaction.
642668
func (t *Transaction) Query(query string) (*QueryResult, error) {
643-
responseJSON, err := t.ExecRequest("", query, "", "")
669+
responseJSON, err := t.ExecRequest("", query, "", "", signingUseNodeDefault)
644670
if err != nil {
645671
return nil, err
646672
}

tests/clients/rustffi/wrapper.go

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import (
3434
"github.com/sourcenetwork/defradb/client/options"
3535
"github.com/sourcenetwork/defradb/crypto"
3636
"github.com/sourcenetwork/defradb/event"
37+
coreblock "github.com/sourcenetwork/defradb/internal/core/block"
3738
"github.com/sourcenetwork/defradb/internal/datastore"
3839
"github.com/sourcenetwork/defradb/internal/utils"
3940
"github.com/sourcenetwork/defradb/tests/clients"
@@ -126,6 +127,30 @@ func execRequestWithIdentity(ident immutable.Option[identity.Identity]) options.
126127
return b
127128
}
128129

130+
// withSigningOverride records a per-operation block signing override on the
131+
// context. Wrapper.ExecRequest and TxnWrapper.ExecRequest must keep the
132+
// client.Store signature, so the override rides the context the same way the
133+
// active transaction already does.
134+
func withSigningOverride(ctx context.Context, enableSigning immutable.Option[bool]) context.Context {
135+
if !enableSigning.HasValue() {
136+
return ctx
137+
}
138+
return coreblock.ContextWithSigning(ctx, enableSigning.Value())
139+
}
140+
141+
// signingOverrideFromContext maps the context override onto the Rust FFI
142+
// convention documented in defra.go.
143+
func signingOverrideFromContext(ctx context.Context) int {
144+
enabled, ok := coreblock.SigningConfigFromContext(ctx)
145+
if !ok {
146+
return signingUseNodeDefault
147+
}
148+
if enabled {
149+
return signingEnabled
150+
}
151+
return signingDisabled
152+
}
153+
129154
// Verify interface compliance at compile time
130155
var _ clients.Client = (*Wrapper)(nil)
131156

@@ -829,7 +854,9 @@ func (w *Wrapper) ExecRequest(
829854
if opt.OperationName.HasValue() {
830855
operationName = opt.OperationName.Value()
831856
}
832-
execResult, err := w.node.ExecRequestFull(identityDID, request, operationName, varsJSON)
857+
execResult, err := w.node.ExecRequestFull(
858+
identityDID, request, operationName, varsJSON, signingOverrideFromContext(ctx),
859+
)
833860
if err != nil {
834861
return &client.RequestResult{
835862
GQL: client.GQLResult{
@@ -2350,7 +2377,9 @@ func (t *TxnWrapper) ExecRequest(
23502377
if opt.OperationName.HasValue() {
23512378
operationName = opt.OperationName.Value()
23522379
}
2353-
responseJSON, err := t.txn.ExecRequest(identityDID, request, operationName, varsJSON)
2380+
responseJSON, err := t.txn.ExecRequest(
2381+
identityDID, request, operationName, varsJSON, signingOverrideFromContext(ctx),
2382+
)
23542383
if err != nil {
23552384
return &client.RequestResult{
23562385
GQL: client.GQLResult{
@@ -2917,6 +2946,7 @@ func (c *CollectionWrapper) AddDocument(ctx context.Context, doc *client.Documen
29172946

29182947
// Extract encryption options
29192948
opt := utils.NewOptions(opts...)
2949+
ctx = withSigningOverride(ctx, opt.EnableSigning)
29202950

29212951
// Convert JSON to GraphQL input format (unquoted keys)
29222952
gqlInput := jsonToGraphQLInput(string(docJSON))
@@ -2973,6 +3003,7 @@ func (c *CollectionWrapper) AddManyDocuments(
29733003

29743004
func (c *CollectionWrapper) UpdateDocument(ctx context.Context, doc *client.Document, opts ...options.Enumerable[options.UpdateDocumentOptions]) error {
29753005
opt := utils.NewOptions(opts...)
3006+
ctx = withSigningOverride(ctx, opt.EnableSigning)
29763007
docJSON, err := doc.ToJSONPatch()
29773008
if err != nil {
29783009
return fmt.Errorf("failed to convert document to JSON: %w", err)
@@ -2992,6 +3023,7 @@ func (c *CollectionWrapper) UpdateDocument(ctx context.Context, doc *client.Docu
29923023

29933024
func (c *CollectionWrapper) SaveDocument(ctx context.Context, doc *client.Document, opts ...options.Enumerable[options.SaveDocumentOptions]) error {
29943025
opt := utils.NewOptions(opts...)
3026+
ctx = withSigningOverride(ctx, opt.EnableSigning)
29953027
// Check if doc exists in the database by querying for it.
29963028
// Pass identity so ACP-protected documents are visible to the owner.
29973029
existsOpt := options.ExistsDocument()
@@ -3082,6 +3114,7 @@ func (c *CollectionWrapper) isDocumentDeleted(ctx context.Context, docID client.
30823114

30833115
func (c *CollectionWrapper) DeleteDocument(ctx context.Context, docID client.DocID, opts ...options.Enumerable[options.DeleteDocumentOptions]) (bool, error) {
30843116
opt := utils.NewOptions(opts...)
3117+
ctx = withSigningOverride(ctx, opt.EnableSigning)
30853118
mutation := fmt.Sprintf(`mutation { delete_%s(docID: "%s") { _docID } }`, c.version.Name, docID.String())
30863119
result := c.execRequest(ctx, mutation, execRequestWithIdentity(opt.GetIdentity()))
30873120
if len(result.GQL.Errors) > 0 {
@@ -3146,6 +3179,7 @@ func (c *CollectionWrapper) UpdateDocumentsWithFilter(
31463179
}
31473180

31483181
opt := utils.NewOptions(opts...)
3182+
ctx = withSigningOverride(ctx, opt.EnableSigning)
31493183
gqlUpdater := jsonToGraphQLInput(updater)
31503184
mutation := fmt.Sprintf(`mutation { update_%s(filter: %s, input: %s) { _docID } }`,
31513185
c.version.Name, gqlFilter, gqlUpdater)
@@ -3453,6 +3487,7 @@ func decodeBase64Deltas(v any) any {
34533487

34543488
func (c *CollectionWrapper) DeleteDocumentsWithFilter(ctx context.Context, filter any, opts ...options.Enumerable[options.DeleteDocumentsWithFilterOptions]) (*client.DeleteResult, error) {
34553489
opt := utils.NewOptions(opts...)
3490+
ctx = withSigningOverride(ctx, opt.EnableSigning)
34563491
gqlFilter, err := mutationFilterToGraphQLInput(filter)
34573492
if err != nil {
34583493
return nil, err

0 commit comments

Comments
 (0)