Skip to content

feat: add mutable protocol-level multisig account support - #27

Draft
digitalSloth wants to merge 1 commit into
mainfrom
feature/multisig
Draft

digitalSloth wants to merge 1 commit into
mainfrom
feature/multisig

Conversation

@digitalSloth

Copy link
Copy Markdown
Owner
  • src/model/primitives/address.ts, index.ts, src/index.ts: add Address.multisigByte/isMultisig/fromMultisigCreation and the MULTISIG_ADDRESS vanity constant.
  • src/model/nom/accountBlock.ts: add optional multisigAuth field to AccountBlockTemplate/AccountBlock with base64 JSON round-trip; omitted entirely from toJson when unset.
  • src/utilities/block.ts: comment-only note in getTxHash documenting that multisigAuth is excluded from the consensus preimage (INV-1); extract validateReceiveBlock out of checkAndSetFields (behavior-preserving, INV-2); add new exported freezeBlock/signBlock/assembleMultisigAuth primitives for both send and receive multisig blocks.
  • src/embedded/multisig.ts, src/model/embedded/multisig.ts, src/api/embedded/multisig.ts: new 12th embedded contract (CreateMultisig/ ChangePolicy ABI, MultisigPolicyInfo/MultisigRecordInfo models, MultisigApi with getPolicy/createMultisig/changePolicy), wired into src/embedded/index.ts, src/model/embedded/index.ts, src/api/embedded/embedded.ts and src/index.ts (MultisigContract only, matching the htlc precedent — MultisigApi is not surfaced top-level).
  • src/client/nodeErrors.ts (new) + src/client/index.ts: central typed node-error registry (mapNodeError) and a flat Znn...Exception hierarchy rooted at ZnnEmbeddedContractException extends ZnnClientException.
  • src/client/http.ts, src/client/websocket.ts, src/api/ledger.ts: route both RPC-error channels and the publishRawTransaction non-null-result channel through mapNodeError.
  • test/api/ledger.spec.ts: update the publish-error assertion to match the new typed-error shape (was asserting the old NETWORK_ERROR message).

- src/model/primitives/address.ts, index.ts, src/index.ts: add
  Address.multisigByte/isMultisigAddress/fromMultisigCreation and the
  MULTISIG_ADDRESS vanity constant.
- src/model/nom/accountBlock.ts: add optional multisigAuth field to
  AccountBlockTemplate/AccountBlock with base64 JSON round-trip; omitted
  entirely from toJson when unset.
- src/utilities/block.ts: document that multisigAuth is excluded from the
  consensus preimage in getTxHash; extract validateReceiveBlock out of
  checkAndSetFields; add exported freezeBlock/signBlock/assembleMultisigAuth
  primitives for send and receive multisig blocks.
- src/embedded/multisig.ts, src/model/embedded/multisig.ts,
  src/api/embedded/multisig.ts: new embedded contract (CreateMultisig/
  ChangePolicy ABI, MultisigPolicyInfo/MultisigRecordInfo models,
  MultisigApi with getPolicy/createMultisig/changePolicy), wired into
  src/embedded/index.ts, src/model/embedded/index.ts,
  src/api/embedded/embedded.ts and src/index.ts.
- src/api/embedded/constants.ts: add MULTISIG_CREATION_FEE_IN_ZNN;
  createMultisig burns this fee instead of 0n, takes a required creator
  address, and throws MultisigCreatorMustBeSingleSigException if the
  creator is itself a multisig address.
- src/client/nodeErrors.ts (new) + src/client/index.ts, src/index.ts:
  central typed node-error registry (mapNodeError) and a flat
  Znn...Exception hierarchy rooted at ZnnEmbeddedContractException extends
  ZnnClientException, covering multisig policy/threshold/authority errors
  and account-block auth errors.
- src/client/http.ts, src/client/websocket.ts, src/api/ledger.ts: route
  RPC errors and the publishRawTransaction non-null-result channel through
  mapNodeError.
- docs/multisig.md, docs/api-overview.md: document address derivation,
  account creation (including the balance precondition for the burn),
  cross-machine signing, signature-collection timing, and error handling.
- test/api/embedded/multisig.spec.ts, test/client/errors.spec.ts,
  test/api/ledger.spec.ts: cover the new contract templates, error
  mappings, and the typed-error publish path.
@digitalSloth
digitalSloth marked this pull request as draft September 20, 2026 17:48
@mrstockholmz

Copy link
Copy Markdown

Reviewed at f1b6e0f. Clean SDK counterpart to the go-zenon#72 multisig work.

The three-phase pipeline (freeze → sign → assemble) is the right shape. Freezing before collecting signatures means every signer signs the identical hash, including across the toJson/fromJson hand-off. The signBlock guard against unfrozen blocks catches the most likely integration mistake.

The hash-invariance tests are the important ones. They pin that multisigAuth (unset, set, mutated, or round-tripped) never changes getTxHash, which matches the node excluding the auth data from the block hash. Omitting multisigAuth from toJson when it is unset keeps existing consumers unaffected.

One doc mismatch with the node side: in go-zenon#72, VerifyThresholdSignatures requires exactly Threshold signatures (len(signatures) != int(policy.Threshold) returns false). The order-independence claim is correct, since the verifier trial-matches against distinct signers. But docs/multisig.md step 4 says "Assemble and publish once >= threshold signatures are collected". A 2-of-3 wallet that collects all three and passes them to assembleMultisigAuth would be rejected with MultisigThresholdMismatchException. Suggest changing the docs to "exactly threshold". Optionally, assembleMultisigAuth could take the threshold, or the doc comment could say that the caller must pass exactly that many signatures.

— mr.stockholm.Z

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants