wire go-synapse signer into keystore (vs go-filsigner), fix Wallet/Actor semantics#622
wire go-synapse signer into keystore (vs go-filsigner), fix Wallet/Actor semantics#622
Conversation
1a3935e to
a494cf5
Compare
|
reviewing this next |
449f85d to
1202206
Compare
|
taking lock on small fixes on this |
Callers (dealpusher, send-manual handler) now resolve the wallet and build a signing closure before calling MakeDeal, keeping the deal maker free of keystore and database concerns.
e5d498a to
ee0f83b
Compare
|
this is definitely going to be a squash merge 😅 |
|
still tweaking this to clarify the wallet<>actor relationship but close |
The model had Preparation associated with Actors (on-chain identities), but the correct invariant is: any deal we make requires a Wallet (private key), and a Wallet optionally has an Actor (for legacy market deals). PDP deals need only a Wallet. Imported/tracked deals may have neither. Core change: Preparation.Actors []Actor → Preparation.Wallets []Wallet with wallet_assignments join table (was actor_assignments with a TODO). Dealpusher market deal path now: choose wallet from Preparation.Wallets, require wallet.ActorID, load actor from DB, sign with wallet key, pass actor to MakeDeal. Previously it went through actors and reverse-looked up wallets. WalletChooser interface unchanged (already took []model.Wallet from prior commit) but DatacapWalletChooser now dereferences wallet.ActorID to find the on-chain client for datacap and pending deal queries. Handler changes: - attach/detach/listattached: operate on Preparation.Wallets directly, find wallet by address or uint ID, return []model.Wallet - schedule/create: preload Wallets, check len(preparation.Wallets) - dataprep/remove: cascade through Wallets association Swagger regenerated: ListAttachedWallets returns Wallet not Actor, ModelActor removed from generated client (unused). All tests updated: fixtures create Actor records for FK constraints (Deal.ClientID references actors table), use Wallets in Preparation fixtures, create wallets individually to avoid gorm batch uniqueIndex conflicts on KeyPath.
handler/wallet/remove.go
Outdated
| var affected int64 | ||
| err := database.DoRetry(ctx, func() error { | ||
| tx := db.Where("address = ? OR id = ?", address, address).Delete(&model.Wallet{}) | ||
| tx := db.Where("address = ? OR id = ?", address, address).Delete(&model.Actor{}) |
There was a problem hiding this comment.
This changes /wallet/{address} removal semantics from deleting model.Wallet to deleting model.Actor.
That looks like an API regression: the wallet endpoint can return success while the wallet row (and keystore reference) remains, so wallet list still shows it. The swagger route/id are still wallet-scoped (RemoveWallet, /wallet/{address}).
Can we either:
- keep this endpoint deleting
model.Wallet(and decide key file behavior), or - rename/split endpoints so actor removal is explicit?
There was a problem hiding this comment.
fair, we will eventually probably want actor-level deletions as well but will fix for correctness at this stage
| KeyStore: "local", | ||
| Address: addr.String(), | ||
| Name: request.Name, | ||
| ActorID: nil, // populated lazily when needed |
There was a problem hiding this comment.
ActorID is intentionally nil here (offline import), but I don’t see the actor-linking flow wired into the runtime path yet.
As written, imported wallets can’t be used for market deals until something else sets ActorID.
There was a problem hiding this comment.
yes this needs a lazy hydration once a message happens which due to invariants will always succeed (wallet funded or has datacap), I was going to wire that in later since the goal was to get this PR stable enough for you to work off of but it's pretty easy so I'll just do it now
service/dealpusher/dealpusher.go
Outdated
| } | ||
|
|
||
| // market deals need the on-chain actor for the deal proposal | ||
| if walletObj.ActorID == nil { |
There was a problem hiding this comment.
This hard-fails schedules when wallet.ActorID == nil.
Given import now supports offline wallets (ActorID=nil), this blocks newly imported wallets from ever progressing unless actor linking happens elsewhere. I only see GetOrCreateActor defined, not used.
Can we resolve/link actor here (or earlier in wallet selection/import flow) before returning schedule error?
handler/deal/send-manual.go
Outdated
|
|
||
| dealModel, err := dealMaker.MakeDeal(ctx, wallet, car, dealConfig) | ||
| // resolve wallet for signing | ||
| walletRecord, err := wallet.LoadWalletByActorID(ctx, db, actor.ID) |
There was a problem hiding this comment.
Manual deal path now requires a pre-linked wallet for the actor (LoadWalletByActorID), so it fails for wallets imported offline and not yet linked.
Should this path also perform lazy actor/wallet linking (similar to GetOrCreateActor) so first use works end-to-end?
| // private key stored in external keystore, can be linked to on-chain actor | ||
| // wallets can exist before actors are created on-chain | ||
| type Wallet struct { | ||
| ID uint `gorm:"primaryKey" json:"id"` |
There was a problem hiding this comment.
model.Wallet now uses a numeric auto-increment PK (ID uint).
Can we also update fixPostgresSequences to include wallets in sequenceTables? Otherwise restored/imported DBs with explicit wallet IDs can end up with stale wallets_id_seq and fail subsequent inserts.
There was a problem hiding this comment.
given that we are still supporting multiple backends I would fix this the same way we fix the other sequences (in admin init on every startup)
tbh I would love to drop mysql support, there is just no reason for it, but sqlite for small/test use cases remains useful
There was a problem hiding this comment.
EDIT: yes ok on closer reading that's exactly what you're suggesting, yes will do
|
Thanks for the large refactor here, this is moving in the right direction. I reviewed with focus on behavior/regression risk in wallet/actor separation and found 3 blocking issues:
I left inline comments at exact code locations for each item. |
|
there are other potential QoL improvements like giving wallets human readable labels etc (some of that was done in the discarded develop branch but with way too much detail) but let's leave that for later |
resolve conflicts in dealpusher: adapt PDP scheduling to new Wallet/Actor model and keystore-based EVMSigner from #622
Problem
Singularity used go-filsigner for Filecoin message signing, with key material stored inline in the database (wallets.private_key column). The Wallet model was actually an on-chain actor identity (f0 address) with an embedded private key -- confusing naming that also coupled key management to database access and left a big security hole/private keys exposed in any db backup or access violation
For PDP deals, we need EVM transaction signing (secp256k1 ECDSA), which go-filsigner doesn't support. The existing model also couldn't represent the real relationship: a wallet (private key) exists before any on-chain actor, and PDP deals don't need an actor at all.
Solution
New keystore abstraction (util/keystore/):
Model rename — old Wallet becomes Actor:
New Wallet model (model/wallet.go):
Preparation associates with Wallet, not Actor:
MakeDeal takes ProposalSigner instead of db + keystore:
Lazy actor resolution (handler/wallet/sign.go):
DealPusher options pattern (service/dealpusher/options.go):
Dependency changes
What's NOT in this PR