diff --git a/sdk/c/include/synor/zk.h b/sdk/c/include/synor/zk.h new file mode 100644 index 0000000..a538af9 --- /dev/null +++ b/sdk/c/include/synor/zk.h @@ -0,0 +1,527 @@ +/** + * Synor ZK SDK for C + * + * Zero-Knowledge proof systems for ZK-Rollups and privacy. + */ + +#ifndef SYNOR_ZK_H +#define SYNOR_ZK_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* ============================================================================ + * Opaque Types + * ============================================================================ */ + +typedef struct synor_zk_client synor_zk_client_t; +typedef struct synor_zk_proof synor_zk_proof_t; +typedef struct synor_zk_verification_key synor_zk_verification_key_t; +typedef struct synor_zk_proving_key synor_zk_proving_key_t; +typedef struct synor_zk_batch synor_zk_batch_t; + +/* ============================================================================ + * Enumerations + * ============================================================================ */ + +/** Proof system backends */ +typedef enum { + SYNOR_ZK_PROOF_SYSTEM_GROTH16 = 0, /**< Smallest proofs (~192 bytes), fastest verification */ + SYNOR_ZK_PROOF_SYSTEM_PLONK = 1, /**< Universal trusted setup, medium proofs (~512 bytes) */ + SYNOR_ZK_PROOF_SYSTEM_STARK = 2 /**< No trusted setup, largest proofs (~50KB) */ +} synor_zk_proof_system_t; + +/** Circuit types for different operations */ +typedef enum { + SYNOR_ZK_CIRCUIT_TRANSFER = 0, /**< Single transfer between accounts */ + SYNOR_ZK_CIRCUIT_BATCH = 1, /**< Batch of multiple transfers */ + SYNOR_ZK_CIRCUIT_DEPOSIT = 2, /**< Deposit from L1 to L2 */ + SYNOR_ZK_CIRCUIT_WITHDRAW = 3 /**< Withdrawal from L2 to L1 */ +} synor_zk_circuit_type_t; + +/** Rollup state */ +typedef enum { + SYNOR_ZK_ROLLUP_STATE_ACTIVE = 0, + SYNOR_ZK_ROLLUP_STATE_PAUSED = 1, + SYNOR_ZK_ROLLUP_STATE_FINALIZING = 2, + SYNOR_ZK_ROLLUP_STATE_FINALIZED = 3 +} synor_zk_rollup_state_t; + +/** Proof status */ +typedef enum { + SYNOR_ZK_PROOF_STATUS_GENERATING = 0, + SYNOR_ZK_PROOF_STATUS_COMPLETED = 1, + SYNOR_ZK_PROOF_STATUS_FAILED = 2, + SYNOR_ZK_PROOF_STATUS_VERIFIED = 3 +} synor_zk_proof_status_t; + +/** Error codes */ +typedef enum { + SYNOR_ZK_OK = 0, + SYNOR_ZK_ERROR_INVALID_ARGUMENT = 1, + SYNOR_ZK_ERROR_NETWORK = 2, + SYNOR_ZK_ERROR_AUTH = 3, + SYNOR_ZK_ERROR_NOT_FOUND = 4, + SYNOR_ZK_ERROR_TIMEOUT = 5, + SYNOR_ZK_ERROR_INTERNAL = 6, + SYNOR_ZK_ERROR_CLIENT_CLOSED = 7, + SYNOR_ZK_ERROR_PROOF_INVALID = 8, + SYNOR_ZK_ERROR_CIRCUIT_ERROR = 9 +} synor_zk_error_t; + +/* ============================================================================ + * Structures + * ============================================================================ */ + +/** SDK configuration */ +typedef struct { + const char* api_key; + const char* endpoint; /**< Default: "https://zk.synor.io/v1" */ + const char* ws_endpoint; /**< Default: "wss://zk.synor.io/v1/ws" */ + uint32_t timeout_ms; /**< Default: 60000 */ + uint32_t retries; /**< Default: 3 */ + synor_zk_proof_system_t default_proof_system; /**< Default: GROTH16 */ + bool debug; /**< Default: false */ +} synor_zk_config_t; + +/** Zero-knowledge proof */ +typedef struct { + char id[64]; + synor_zk_proof_system_t system; + synor_zk_circuit_type_t circuit_type; + uint8_t* data; + size_t data_len; + char** public_inputs; + size_t public_inputs_count; + size_t size; + uint64_t generation_time_ms; + uint64_t created_at; +} synor_zk_proof_info_t; + +/** Verification result */ +typedef struct { + bool valid; + uint64_t verification_time_ms; + char* error; +} synor_zk_verification_result_t; + +/** Account state */ +typedef struct { + char address[64]; + char balance[32]; + uint64_t nonce; + char pubkey_hash[66]; +} synor_zk_account_state_t; + +/** Transfer operation */ +typedef struct { + char from[64]; + char to[64]; + char amount[32]; + char fee[32]; + uint64_t nonce; + char* signature; /**< Optional */ +} synor_zk_transfer_t; + +/** Deposit operation */ +typedef struct { + char l1_tx_hash[66]; + char recipient[64]; + char amount[32]; + char* token; /**< Optional */ +} synor_zk_deposit_t; + +/** Withdrawal operation */ +typedef struct { + char sender[64]; + char recipient[64]; + char amount[32]; + char* token; /**< Optional */ + uint64_t nonce; + char* signature; /**< Optional */ +} synor_zk_withdrawal_t; + +/** Rollup statistics */ +typedef struct { + uint64_t current_batch; + uint64_t total_transactions; + uint64_t total_proofs; + uint64_t avg_proof_time_ms; + char state_root[66]; + uint64_t account_count; + char tvl[32]; +} synor_zk_rollup_stats_t; + +/** Proof request */ +typedef struct { + synor_zk_circuit_type_t circuit_type; + char** public_inputs; + size_t public_inputs_count; + char** private_inputs; + size_t private_inputs_count; + synor_zk_proof_system_t* system; /**< Optional, uses default if NULL */ +} synor_zk_proof_request_t; + +/** Merkle proof */ +typedef struct { + char leaf[66]; + char** path; + size_t path_len; + uint8_t* indices; + size_t indices_len; + char root[66]; +} synor_zk_merkle_proof_t; + +/** Ceremony contribution */ +typedef struct { + char contributor_id[64]; + char hash[66]; + uint64_t timestamp; + bool verified; +} synor_zk_ceremony_contribution_t; + +/** Trusted setup ceremony */ +typedef struct { + char id[64]; + synor_zk_circuit_type_t circuit_type; + synor_zk_proof_system_t system; + size_t contribution_count; + char latest_hash[66]; + bool complete; + synor_zk_ceremony_contribution_t* contributions; + size_t contributions_count; +} synor_zk_trusted_setup_t; + +/** Proof system characteristics */ +typedef struct { + const char* name; + size_t proof_size; + uint32_t verification_time_ms; + bool trusted_setup; + bool universal; +} synor_zk_proof_system_info_t; + +/* ============================================================================ + * Client Lifecycle + * ============================================================================ */ + +/** + * Create a new ZK SDK client + * + * @param config Configuration options + * @return New client handle, or NULL on error + */ +synor_zk_client_t* synor_zk_client_new(const synor_zk_config_t* config); + +/** + * Close and free a client + * + * @param client Client to close + */ +void synor_zk_client_free(synor_zk_client_t* client); + +/** + * Check if client is closed + * + * @param client Client handle + * @return true if closed + */ +bool synor_zk_client_is_closed(const synor_zk_client_t* client); + +/** + * Health check + * + * @param client Client handle + * @param healthy Output parameter for health status + * @return Error code + */ +synor_zk_error_t synor_zk_health_check(synor_zk_client_t* client, bool* healthy); + +/* ============================================================================ + * Proof Operations + * ============================================================================ */ + +/** + * Generate a zero-knowledge proof + * + * @param client Client handle + * @param request Proof request parameters + * @param proof Output parameter for generated proof + * @return Error code + */ +synor_zk_error_t synor_zk_proof_generate( + synor_zk_client_t* client, + const synor_zk_proof_request_t* request, + synor_zk_proof_t** proof +); + +/** + * Verify a proof + * + * @param client Client handle + * @param proof Proof to verify + * @param result Output parameter for verification result + * @return Error code + */ +synor_zk_error_t synor_zk_proof_verify( + synor_zk_client_t* client, + const synor_zk_proof_t* proof, + synor_zk_verification_result_t* result +); + +/** + * Get a proof by ID + * + * @param client Client handle + * @param proof_id Proof identifier + * @param proof Output parameter for proof + * @return Error code + */ +synor_zk_error_t synor_zk_proof_get( + synor_zk_client_t* client, + const char* proof_id, + synor_zk_proof_t** proof +); + +/** + * Free a proof + */ +void synor_zk_proof_free(synor_zk_proof_t* proof); + +/** + * Get proof info + */ +synor_zk_error_t synor_zk_proof_get_info( + const synor_zk_proof_t* proof, + synor_zk_proof_info_t* info +); + +/* ============================================================================ + * Circuit Operations + * ============================================================================ */ + +/** + * Get verification key for a circuit + */ +synor_zk_error_t synor_zk_circuit_get_vk( + synor_zk_client_t* client, + synor_zk_circuit_type_t circuit_type, + synor_zk_proof_system_t system, + synor_zk_verification_key_t** vk +); + +/** + * Get proving key for a circuit + */ +synor_zk_error_t synor_zk_circuit_get_pk( + synor_zk_client_t* client, + synor_zk_circuit_type_t circuit_type, + synor_zk_proof_system_t system, + synor_zk_proving_key_t** pk +); + +/** + * Free verification key + */ +void synor_zk_verification_key_free(synor_zk_verification_key_t* vk); + +/** + * Free proving key + */ +void synor_zk_proving_key_free(synor_zk_proving_key_t* pk); + +/* ============================================================================ + * Rollup Operations + * ============================================================================ */ + +/** + * Get rollup statistics + */ +synor_zk_error_t synor_zk_rollup_get_stats( + synor_zk_client_t* client, + synor_zk_rollup_stats_t* stats +); + +/** + * Get current batch + */ +synor_zk_error_t synor_zk_rollup_get_current_batch( + synor_zk_client_t* client, + synor_zk_batch_t** batch +); + +/** + * Get batch by number + */ +synor_zk_error_t synor_zk_rollup_get_batch( + synor_zk_client_t* client, + uint64_t batch_number, + synor_zk_batch_t** batch +); + +/** + * Submit a transfer + */ +synor_zk_error_t synor_zk_rollup_submit_transfer( + synor_zk_client_t* client, + const synor_zk_transfer_t* transfer, + char* tx_id, + size_t tx_id_len +); + +/** + * Submit a deposit + */ +synor_zk_error_t synor_zk_rollup_submit_deposit( + synor_zk_client_t* client, + const synor_zk_deposit_t* deposit, + char* tx_id, + size_t tx_id_len +); + +/** + * Submit a withdrawal + */ +synor_zk_error_t synor_zk_rollup_submit_withdrawal( + synor_zk_client_t* client, + const synor_zk_withdrawal_t* withdrawal, + char* tx_id, + size_t tx_id_len +); + +/** + * Finalize current batch + */ +synor_zk_error_t synor_zk_rollup_finalize_batch( + synor_zk_client_t* client, + synor_zk_batch_t** batch +); + +/** + * Free batch + */ +void synor_zk_batch_free(synor_zk_batch_t* batch); + +/* ============================================================================ + * State Operations + * ============================================================================ */ + +/** + * Get current state root + */ +synor_zk_error_t synor_zk_state_get_root( + synor_zk_client_t* client, + char* root, + size_t root_len +); + +/** + * Get account state + */ +synor_zk_error_t synor_zk_state_get_account( + synor_zk_client_t* client, + const char* address, + synor_zk_account_state_t* account +); + +/** + * Get merkle proof for account inclusion + */ +synor_zk_error_t synor_zk_state_get_merkle_proof( + synor_zk_client_t* client, + const char* address, + synor_zk_merkle_proof_t* proof +); + +/** + * Verify merkle proof + */ +synor_zk_error_t synor_zk_state_verify_merkle_proof( + synor_zk_client_t* client, + const synor_zk_merkle_proof_t* proof, + bool* valid +); + +/** + * Free merkle proof + */ +void synor_zk_merkle_proof_free(synor_zk_merkle_proof_t* proof); + +/* ============================================================================ + * Ceremony Operations + * ============================================================================ */ + +/** + * Get ceremony status + */ +synor_zk_error_t synor_zk_ceremony_get_status( + synor_zk_client_t* client, + synor_zk_circuit_type_t circuit_type, + synor_zk_proof_system_t system, + synor_zk_trusted_setup_t* setup +); + +/** + * Contribute to ceremony + */ +synor_zk_error_t synor_zk_ceremony_contribute( + synor_zk_client_t* client, + synor_zk_circuit_type_t circuit_type, + const uint8_t* entropy, + size_t entropy_len, + synor_zk_proof_system_t system, + synor_zk_ceremony_contribution_t* contribution +); + +/** + * Verify a contribution + */ +synor_zk_error_t synor_zk_ceremony_verify_contribution( + synor_zk_client_t* client, + synor_zk_circuit_type_t circuit_type, + const char* contribution_hash, + bool* valid +); + +/** + * Free trusted setup + */ +void synor_zk_trusted_setup_free(synor_zk_trusted_setup_t* setup); + +/* ============================================================================ + * Utility Functions + * ============================================================================ */ + +/** + * Get proof system info + */ +synor_zk_proof_system_info_t synor_zk_get_proof_system_info(synor_zk_proof_system_t system); + +/** + * Get error message + */ +const char* synor_zk_error_message(synor_zk_error_t error); + +/** + * Free verification result + */ +void synor_zk_verification_result_free(synor_zk_verification_result_t* result); + +/* ============================================================================ + * Constants + * ============================================================================ */ + +#define SYNOR_ZK_MAX_BATCH_SIZE 1000 +#define SYNOR_ZK_STATE_TREE_DEPTH 32 +#define SYNOR_ZK_PROOF_EXPIRY_BLOCKS 100 + +#ifdef __cplusplus +} +#endif + +#endif /* SYNOR_ZK_H */ diff --git a/sdk/cpp/include/synor/zk.hpp b/sdk/cpp/include/synor/zk.hpp new file mode 100644 index 0000000..6a5b612 --- /dev/null +++ b/sdk/cpp/include/synor/zk.hpp @@ -0,0 +1,429 @@ +/** + * Synor ZK SDK for C++ + * + * Zero-Knowledge proof systems for ZK-Rollups and privacy. + */ + +#ifndef SYNOR_ZK_HPP +#define SYNOR_ZK_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace synor { +namespace zk { + +/* ============================================================================ + * Enumerations + * ============================================================================ */ + +/** Proof system backends */ +enum class ProofSystem { + Groth16, ///< Smallest proofs (~192 bytes), fastest verification + Plonk, ///< Universal trusted setup, medium proofs (~512 bytes) + Stark ///< No trusted setup, largest proofs (~50KB) +}; + +/** Circuit types for different operations */ +enum class CircuitType { + Transfer, ///< Single transfer between accounts + Batch, ///< Batch of multiple transfers + Deposit, ///< Deposit from L1 to L2 + Withdraw ///< Withdrawal from L2 to L1 +}; + +/** Rollup state */ +enum class RollupState { + Active, + Paused, + Finalizing, + Finalized +}; + +/** Proof status */ +enum class ProofStatus { + Generating, + Completed, + Failed, + Verified +}; + +/* ============================================================================ + * Exception + * ============================================================================ */ + +class ZkError : public std::runtime_error { +public: + ZkError(const std::string& message, + const std::optional& code = std::nullopt, + const std::optional& status = std::nullopt) + : std::runtime_error(message), code_(code), status_(status) {} + + const std::optional& code() const { return code_; } + const std::optional& status() const { return status_; } + +private: + std::optional code_; + std::optional status_; +}; + +/* ============================================================================ + * Data Types + * ============================================================================ */ + +/** Zero-knowledge proof */ +struct Proof { + std::string id; + ProofSystem system; + CircuitType circuit_type; + std::vector data; + std::vector public_inputs; + size_t size; + uint64_t generation_time_ms; + uint64_t created_at; +}; + +/** Verification key for a circuit */ +struct VerificationKey { + std::string circuit_id; + CircuitType circuit_type; + ProofSystem system; + std::vector data; + size_t size; +}; + +/** Proving key for generating proofs */ +struct ProvingKey { + std::string circuit_id; + CircuitType circuit_type; + ProofSystem system; + std::vector data; + size_t size; +}; + +/** Circuit configuration */ +struct CircuitConfig { + CircuitType type; + std::optional max_batch_size; + std::optional tree_depth; + std::optional verify_signatures; + std::optional> custom_constraints; +}; + +/** Account state in the rollup */ +struct AccountState { + std::string address; + std::string balance; + uint64_t nonce; + std::string pubkey_hash; +}; + +/** Transfer operation */ +struct Transfer { + std::string from; + std::string to; + std::string amount; + std::string fee; + uint64_t nonce; + std::optional signature; +}; + +/** Deposit operation (L1 -> L2) */ +struct Deposit { + std::string l1_tx_hash; + std::string recipient; + std::string amount; + std::optional token; +}; + +/** Withdrawal operation (L2 -> L1) */ +struct Withdrawal { + std::string sender; + std::string recipient; + std::string amount; + std::optional token; + uint64_t nonce; + std::optional signature; +}; + +/** Rollup batch */ +struct Batch { + uint64_t batch_number; + std::string prev_state_root; + std::string new_state_root; + std::vector transfers; + std::vector deposits; + std::vector withdrawals; + std::optional proof; + uint64_t timestamp; +}; + +/** Rollup statistics */ +struct RollupStats { + uint64_t current_batch; + uint64_t total_transactions; + uint64_t total_proofs; + uint64_t avg_proof_time_ms; + std::string state_root; + uint64_t account_count; + std::string tvl; +}; + +/** Proof request */ +struct ProofRequest { + CircuitType circuit_type; + std::vector public_inputs; + std::vector private_inputs; + std::optional system; +}; + +/** Verification result */ +struct VerificationResult { + bool valid; + uint64_t verification_time_ms; + std::optional error; +}; + +/** Merkle proof for state inclusion */ +struct MerkleProof { + std::string leaf; + std::vector path; + std::vector indices; + std::string root; +}; + +/** Ceremony contribution */ +struct CeremonyContribution { + std::string contributor_id; + std::string hash; + uint64_t timestamp; + bool verified; +}; + +/** Trusted setup ceremony */ +struct TrustedSetup { + std::string id; + CircuitType circuit_type; + ProofSystem system; + size_t contribution_count; + std::string latest_hash; + bool complete; + std::vector contributions; +}; + +/** SDK configuration */ +struct ZkConfig { + std::string api_key; + std::string endpoint = "https://zk.synor.io/v1"; + std::string ws_endpoint = "wss://zk.synor.io/v1/ws"; + std::chrono::milliseconds timeout{60000}; + int retries = 3; + ProofSystem default_proof_system = ProofSystem::Groth16; + bool debug = false; +}; + +/** Proof system characteristics */ +struct ProofSystemInfo { + std::string name; + size_t proof_size; + uint32_t verification_time_ms; + bool trusted_setup; + bool universal; +}; + +/* ============================================================================ + * Client Classes + * ============================================================================ */ + +class SynorZk; + +/** Proofs sub-client */ +class ProofsClient { +public: + explicit ProofsClient(SynorZk& zk) : zk_(zk) {} + + std::future generate(const ProofRequest& request); + std::future verify(const Proof& proof); + std::future get(const std::string& proof_id); + std::future> list(std::optional limit = std::nullopt, + std::optional offset = std::nullopt); + std::future> serialize(const Proof& proof); + std::future deserialize(const std::vector& data, ProofSystem system); + +private: + SynorZk& zk_; +}; + +/** Circuits sub-client */ +class CircuitsClient { +public: + explicit CircuitsClient(SynorZk& zk) : zk_(zk) {} + + std::future getVerificationKey(CircuitType circuit_type, + std::optional system = std::nullopt); + std::future getProvingKey(CircuitType circuit_type, + std::optional system = std::nullopt); + std::future> list(); + std::future getConfig(CircuitType circuit_type); + std::future compile(const CircuitConfig& config); + +private: + SynorZk& zk_; +}; + +/** Rollup sub-client */ +class RollupClient { +public: + explicit RollupClient(SynorZk& zk) : zk_(zk) {} + + std::future getStats(); + std::future getCurrentBatch(); + std::future getBatch(uint64_t batch_number); + std::future submitTransfer(const Transfer& transfer); + std::future submitDeposit(const Deposit& deposit); + std::future submitWithdrawal(const Withdrawal& withdrawal); + std::future finalizeBatch(); + std::future> getPendingTransactions(); + +private: + SynorZk& zk_; +}; + +/** State sub-client */ +class StateClient { +public: + explicit StateClient(SynorZk& zk) : zk_(zk) {} + + std::future getRoot(); + std::future getAccount(const std::string& address); + std::future getMerkleProof(const std::string& address); + std::future verifyMerkleProof(const MerkleProof& proof); + std::future> getStateAtBatch(uint64_t batch_number); + +private: + SynorZk& zk_; +}; + +/** Ceremony sub-client */ +class CeremonyClient { +public: + explicit CeremonyClient(SynorZk& zk) : zk_(zk) {} + + std::future getStatus(CircuitType circuit_type, + std::optional system = std::nullopt); + std::future contribute(CircuitType circuit_type, + const std::vector& entropy, + std::optional system = std::nullopt); + std::future verifyContribution(CircuitType circuit_type, + const std::string& contribution_hash); + std::future> listContributions(CircuitType circuit_type); + +private: + SynorZk& zk_; +}; + +/** + * Synor ZK SDK Client + * + * Zero-Knowledge proof systems for ZK-Rollups and privacy. + * + * Example: + * @code + * synor::zk::ZkConfig config; + * config.api_key = "your-api-key"; + * synor::zk::SynorZk zk(config); + * + * // Generate a proof + * synor::zk::ProofRequest request; + * request.circuit_type = synor::zk::CircuitType::Transfer; + * request.public_inputs = {...}; + * request.private_inputs = {...}; + * auto proof = zk.proofs().generate(request).get(); + * + * // Verify the proof + * auto result = zk.proofs().verify(proof).get(); + * std::cout << "Valid: " << result.valid << std::endl; + * @endcode + */ +class SynorZk { +public: + explicit SynorZk(const ZkConfig& config); + ~SynorZk(); + + // Non-copyable + SynorZk(const SynorZk&) = delete; + SynorZk& operator=(const SynorZk&) = delete; + + // Movable + SynorZk(SynorZk&&) noexcept; + SynorZk& operator=(SynorZk&&) noexcept; + + /** Get the default proof system */ + ProofSystem defaultProofSystem() const { return config_.default_proof_system; } + + /** Health check */ + std::future healthCheck(); + + /** Get service info */ + std::future> getInfo(); + + /** Close the client */ + void close(); + + /** Check if client is closed */ + bool isClosed() const { return closed_; } + + /** Sub-clients */ + ProofsClient& proofs() { return proofs_; } + CircuitsClient& circuits() { return circuits_; } + RollupClient& rollup() { return rollup_; } + StateClient& state() { return state_; } + CeremonyClient& ceremony() { return ceremony_; } + + // Internal HTTP methods (public for sub-clients) + std::future> get(const std::string& path); + std::future> post(const std::string& path, + const std::map& body); + +private: + ZkConfig config_; + bool closed_ = false; + ProofsClient proofs_; + CircuitsClient circuits_; + RollupClient rollup_; + StateClient state_; + CeremonyClient ceremony_; +}; + +/* ============================================================================ + * Utility Functions + * ============================================================================ */ + +/** Get proof system info */ +ProofSystemInfo getProofSystemInfo(ProofSystem system); + +/** Convert proof system to string */ +std::string proofSystemToString(ProofSystem system); + +/** Convert circuit type to string */ +std::string circuitTypeToString(CircuitType type); + +/* ============================================================================ + * Constants + * ============================================================================ */ + +constexpr size_t MAX_BATCH_SIZE = 1000; +constexpr size_t STATE_TREE_DEPTH = 32; +constexpr uint64_t PROOF_EXPIRY_BLOCKS = 100; + +} // namespace zk +} // namespace synor + +#endif // SYNOR_ZK_HPP diff --git a/sdk/csharp/src/Synor.Zk/SynorZk.cs b/sdk/csharp/src/Synor.Zk/SynorZk.cs new file mode 100644 index 0000000..05e349b --- /dev/null +++ b/sdk/csharp/src/Synor.Zk/SynorZk.cs @@ -0,0 +1,339 @@ +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Net.Http.Json; +using System.Text.Json; +using System.Text.Json.Serialization; +using System.Threading; +using System.Threading.Tasks; + +namespace Synor.Zk; + +/// +/// Synor ZK SDK for C#/.NET +/// +/// Zero-Knowledge proof systems for ZK-Rollups and privacy. +/// +public class SynorZk : IDisposable +{ + private readonly ZkConfig _config; + private readonly HttpClient _httpClient; + private readonly JsonSerializerOptions _jsonOptions; + private bool _closed; + + public ProofsClient Proofs { get; } + public CircuitsClient Circuits { get; } + public RollupClient Rollup { get; } + public StateClient State { get; } + public CeremonyClient Ceremony { get; } + + public SynorZk(ZkConfig config) + { + _config = config; + _httpClient = new HttpClient + { + BaseAddress = new Uri(config.Endpoint), + Timeout = TimeSpan.FromMilliseconds(config.Timeout) + }; + _httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {config.ApiKey}"); + _httpClient.DefaultRequestHeaders.Add("X-SDK-Version", "csharp/0.1.0"); + + _jsonOptions = new JsonSerializerOptions + { + PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower, + Converters = { new JsonStringEnumConverter(JsonNamingPolicy.SnakeCaseLower) } + }; + + Proofs = new ProofsClient(this); + Circuits = new CircuitsClient(this); + Rollup = new RollupClient(this); + State = new StateClient(this); + Ceremony = new CeremonyClient(this); + } + + public ProofSystem DefaultProofSystem => _config.DefaultProofSystem; + + public async Task HealthCheckAsync(CancellationToken ct = default) + { + try + { + var result = await GetAsync>("/health", ct); + return result.TryGetValue("status", out var status) && status?.ToString() == "healthy"; + } + catch + { + return false; + } + } + + public Task> GetInfoAsync(CancellationToken ct = default) => + GetAsync>("/info", ct); + + public void Close() + { + _closed = true; + _httpClient.Dispose(); + } + + public bool IsClosed => _closed; + + public void Dispose() + { + Close(); + GC.SuppressFinalize(this); + } + + internal async Task GetAsync(string path, CancellationToken ct = default) + { + CheckClosed(); + var response = await _httpClient.GetAsync(path, ct); + return await HandleResponseAsync(response); + } + + internal async Task PostAsync(string path, object? body = null, CancellationToken ct = default) + { + CheckClosed(); + var content = body != null + ? JsonContent.Create(body, options: _jsonOptions) + : JsonContent.Create(new { }); + var response = await _httpClient.PostAsync(path, content, ct); + return await HandleResponseAsync(response); + } + + private async Task HandleResponseAsync(HttpResponseMessage response) + { + var json = await response.Content.ReadAsStringAsync(); + if (!response.IsSuccessStatusCode) + { + var error = JsonSerializer.Deserialize>(json, _jsonOptions) ?? new(); + throw new ZkException( + error.TryGetValue("message", out var msg) ? msg?.ToString() ?? $"HTTP {(int)response.StatusCode}" : $"HTTP {(int)response.StatusCode}", + error.TryGetValue("code", out var code) ? code?.ToString() : null, + (int)response.StatusCode + ); + } + return JsonSerializer.Deserialize(json, _jsonOptions)!; + } + + private void CheckClosed() + { + if (_closed) throw new ZkException("Client has been closed", "CLIENT_CLOSED"); + } +} + +/// Proofs sub-client +public class ProofsClient +{ + private readonly SynorZk _zk; + + internal ProofsClient(SynorZk zk) => _zk = zk; + + public async Task GenerateAsync(ProofRequest request, CancellationToken ct = default) + { + var body = new Dictionary + { + ["circuit_type"] = request.CircuitType.ToApiString(), + ["public_inputs"] = request.PublicInputs, + ["private_inputs"] = request.PrivateInputs, + ["system"] = (request.System ?? _zk.DefaultProofSystem).ToApiString() + }; + return await _zk.PostAsync("/proofs/generate", body, ct); + } + + public async Task VerifyAsync(Proof proof, CancellationToken ct = default) + { + var body = new Dictionary + { + ["proof_id"] = proof.Id, + ["data"] = proof.Data, + ["public_inputs"] = proof.PublicInputs, + ["system"] = proof.System.ToApiString(), + ["circuit_type"] = proof.CircuitType.ToApiString() + }; + return await _zk.PostAsync("/proofs/verify", body, ct); + } + + public Task GetAsync(string proofId, CancellationToken ct = default) => + _zk.GetAsync($"/proofs/{proofId}", ct); + + public async Task> ListAsync(int? limit = null, int? offset = null, CancellationToken ct = default) + { + var path = "/proofs"; + var qs = new List(); + if (limit.HasValue) qs.Add($"limit={limit}"); + if (offset.HasValue) qs.Add($"offset={offset}"); + if (qs.Count > 0) path += "?" + string.Join("&", qs); + + var result = await _zk.GetAsync(path, ct); + return result.Proofs ?? new List(); + } + + public async Task SerializeAsync(Proof proof, CancellationToken ct = default) + { + var result = await _zk.PostAsync("/proofs/serialize", new { proof_id = proof.Id }, ct); + return Convert.FromBase64String(result.Data); + } + + public Task DeserializeAsync(byte[] data, ProofSystem system, CancellationToken ct = default) => + _zk.PostAsync("/proofs/deserialize", new { data = Convert.ToBase64String(data), system = system.ToApiString() }, ct); +} + +/// Circuits sub-client +public class CircuitsClient +{ + private readonly SynorZk _zk; + + internal CircuitsClient(SynorZk zk) => _zk = zk; + + public Task GetVerificationKeyAsync(CircuitType circuitType, ProofSystem? system = null, CancellationToken ct = default) + { + var sys = system ?? _zk.DefaultProofSystem; + return _zk.GetAsync($"/circuits/{circuitType.ToApiString()}/vk?system={sys.ToApiString()}", ct); + } + + public Task GetProvingKeyAsync(CircuitType circuitType, ProofSystem? system = null, CancellationToken ct = default) + { + var sys = system ?? _zk.DefaultProofSystem; + return _zk.GetAsync($"/circuits/{circuitType.ToApiString()}/pk?system={sys.ToApiString()}", ct); + } + + public async Task> ListAsync(CancellationToken ct = default) + { + var result = await _zk.GetAsync("/circuits", ct); + return result.Circuits ?? new List(); + } + + public Task GetConfigAsync(CircuitType circuitType, CancellationToken ct = default) => + _zk.GetAsync($"/circuits/{circuitType.ToApiString()}/config", ct); + + public async Task CompileAsync(CircuitConfig config, CancellationToken ct = default) + { + var result = await _zk.PostAsync("/circuits/compile", config, ct); + return result.CircuitId; + } +} + +/// Rollup sub-client +public class RollupClient +{ + private readonly SynorZk _zk; + + internal RollupClient(SynorZk zk) => _zk = zk; + + public Task GetStatsAsync(CancellationToken ct = default) => + _zk.GetAsync("/rollup/stats", ct); + + public Task GetCurrentBatchAsync(CancellationToken ct = default) => + _zk.GetAsync("/rollup/batch/current", ct); + + public Task GetBatchAsync(long batchNumber, CancellationToken ct = default) => + _zk.GetAsync($"/rollup/batch/{batchNumber}", ct); + + public async Task SubmitTransferAsync(Transfer transfer, CancellationToken ct = default) + { + var result = await _zk.PostAsync("/rollup/transfer", transfer, ct); + return result.TxId; + } + + public async Task SubmitDepositAsync(Deposit deposit, CancellationToken ct = default) + { + var result = await _zk.PostAsync("/rollup/deposit", deposit, ct); + return result.TxId; + } + + public async Task SubmitWithdrawalAsync(Withdrawal withdrawal, CancellationToken ct = default) + { + var result = await _zk.PostAsync("/rollup/withdraw", withdrawal, ct); + return result.TxId; + } + + public Task FinalizeBatchAsync(CancellationToken ct = default) => + _zk.PostAsync("/rollup/batch/finalize", null, ct); + + public async Task> GetPendingTransactionsAsync(CancellationToken ct = default) + { + var result = await _zk.GetAsync("/rollup/pending", ct); + return result.Transactions ?? new List(); + } +} + +/// State sub-client +public class StateClient +{ + private readonly SynorZk _zk; + + internal StateClient(SynorZk zk) => _zk = zk; + + public async Task GetRootAsync(CancellationToken ct = default) + { + var result = await _zk.GetAsync("/state/root", ct); + return result.Root; + } + + public Task GetAccountAsync(string address, CancellationToken ct = default) => + _zk.GetAsync($"/state/account/{address}", ct); + + public Task GetMerkleProofAsync(string address, CancellationToken ct = default) => + _zk.GetAsync($"/state/proof/{address}", ct); + + public async Task VerifyMerkleProofAsync(MerkleProof proof, CancellationToken ct = default) + { + var result = await _zk.PostAsync("/state/proof/verify", proof, ct); + return result.Valid; + } + + public Task> GetStateAtBatchAsync(long batchNumber, CancellationToken ct = default) => + _zk.GetAsync>($"/state/batch/{batchNumber}", ct); +} + +/// Ceremony sub-client +public class CeremonyClient +{ + private readonly SynorZk _zk; + + internal CeremonyClient(SynorZk zk) => _zk = zk; + + public Task GetStatusAsync(CircuitType circuitType, ProofSystem? system = null, CancellationToken ct = default) + { + var sys = system ?? _zk.DefaultProofSystem; + return _zk.GetAsync($"/ceremony/{circuitType.ToApiString()}?system={sys.ToApiString()}", ct); + } + + public Task ContributeAsync(CircuitType circuitType, byte[] entropy, ProofSystem? system = null, CancellationToken ct = default) + { + var sys = system ?? _zk.DefaultProofSystem; + return _zk.PostAsync("/ceremony/contribute", new + { + circuit_type = circuitType.ToApiString(), + entropy = Convert.ToBase64String(entropy), + system = sys.ToApiString() + }, ct); + } + + public async Task VerifyContributionAsync(CircuitType circuitType, string contributionHash, CancellationToken ct = default) + { + var result = await _zk.PostAsync("/ceremony/verify", new + { + circuit_type = circuitType.ToApiString(), + contribution_hash = contributionHash + }, ct); + return result.Valid; + } + + public async Task> ListContributionsAsync(CircuitType circuitType, CancellationToken ct = default) + { + var result = await _zk.GetAsync($"/ceremony/{circuitType.ToApiString()}/contributions", ct); + return result.Contributions ?? new List(); + } +} + +// Response helper types +internal record ProofsListResponse(List? Proofs); +internal record CircuitsListResponse(List? Circuits); +internal record TransactionsListResponse(List? Transactions); +internal record ContributionsListResponse(List? Contributions); +internal record SerializeResponse(string Data); +internal record CircuitIdResponse(string CircuitId); +internal record TxIdResponse(string TxId); +internal record RootResponse(string Root); +internal record ValidResponse(bool Valid); diff --git a/sdk/csharp/src/Synor.Zk/Types.cs b/sdk/csharp/src/Synor.Zk/Types.cs new file mode 100644 index 0000000..d337ddc --- /dev/null +++ b/sdk/csharp/src/Synor.Zk/Types.cs @@ -0,0 +1,246 @@ +using System; +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace Synor.Zk; + +/// Proof system backends +public enum ProofSystem +{ + /// Smallest proofs (~192 bytes), fastest verification + Groth16, + /// Universal trusted setup, medium proofs (~512 bytes) + Plonk, + /// No trusted setup, largest proofs (~50KB) + Stark +} + +/// Circuit types for different operations +public enum CircuitType +{ + /// Single transfer between accounts + Transfer, + /// Batch of multiple transfers + Batch, + /// Deposit from L1 to L2 + Deposit, + /// Withdrawal from L2 to L1 + Withdraw +} + +/// Rollup state +public enum RollupState { Active, Paused, Finalizing, Finalized } + +/// Proof status +public enum ProofStatus { Generating, Completed, Failed, Verified } + +/// Zero-knowledge proof +public record Proof( + string Id, + ProofSystem System, + CircuitType CircuitType, + string Data, + List PublicInputs, + int Size, + long GenerationTimeMs, + long CreatedAt +); + +/// Verification key for a circuit +public record VerificationKey( + string CircuitId, + CircuitType CircuitType, + ProofSystem System, + string Data, + int Size +); + +/// Proving key for generating proofs +public record ProvingKey( + string CircuitId, + CircuitType CircuitType, + ProofSystem System, + string Data, + int Size +); + +/// Circuit configuration +public record CircuitConfig( + CircuitType Type, + int? MaxBatchSize = null, + int? TreeDepth = null, + bool? VerifySignatures = null, + Dictionary? CustomConstraints = null +); + +/// Account state in the rollup +public record AccountState( + string Address, + string Balance, + long Nonce, + string PubkeyHash +); + +/// Transfer operation +public record Transfer( + string From, + string To, + string Amount, + string Fee, + long Nonce, + string? Signature = null +); + +/// Deposit operation (L1 -> L2) +public record Deposit( + string L1TxHash, + string Recipient, + string Amount, + string? Token = null +); + +/// Withdrawal operation (L2 -> L1) +public record Withdrawal( + string Sender, + string Recipient, + string Amount, + string? Token, + long Nonce, + string? Signature = null +); + +/// Rollup batch +public record Batch( + long BatchNumber, + string PrevStateRoot, + string NewStateRoot, + List Transfers, + List Deposits, + List Withdrawals, + Proof? Proof, + long Timestamp +); + +/// Rollup statistics +public record RollupStats( + long CurrentBatch, + long TotalTransactions, + long TotalProofs, + long AvgProofTimeMs, + string StateRoot, + long AccountCount, + string Tvl +); + +/// Proof request +public record ProofRequest( + CircuitType CircuitType, + List PublicInputs, + List PrivateInputs, + ProofSystem? System = null +); + +/// Verification result +public record VerificationResult( + bool Valid, + long VerificationTimeMs, + string? Error = null +); + +/// Merkle proof for state inclusion +public record MerkleProof( + string Leaf, + List Path, + List Indices, + string Root +); + +/// Ceremony contribution +public record CeremonyContribution( + string ContributorId, + string Hash, + long Timestamp, + bool Verified +); + +/// Trusted setup ceremony +public record TrustedSetup( + string Id, + CircuitType CircuitType, + ProofSystem System, + int ContributionCount, + string LatestHash, + bool Complete, + List Contributions +); + +/// ZK SDK configuration +public record ZkConfig( + string ApiKey, + string Endpoint = "https://zk.synor.io/v1", + string WsEndpoint = "wss://zk.synor.io/v1/ws", + int Timeout = 60000, + int Retries = 3, + ProofSystem DefaultProofSystem = ProofSystem.Groth16, + bool Debug = false +); + +/// ZK SDK exception +public class ZkException : Exception +{ + public string? Code { get; } + public int? Status { get; } + + public ZkException(string message, string? code = null, int? status = null) + : base(message) + { + Code = code; + Status = status; + } +} + +/// Proof system characteristics +public record ProofSystemInfo( + string Name, + int ProofSize, + int VerificationTimeMs, + bool TrustedSetup, + bool Universal +); + +/// Extension methods +public static class ZkExtensions +{ + public static string ToApiString(this ProofSystem system) => system switch + { + ProofSystem.Groth16 => "groth16", + ProofSystem.Plonk => "plonk", + ProofSystem.Stark => "stark", + _ => "groth16" + }; + + public static string ToApiString(this CircuitType type) => type switch + { + CircuitType.Transfer => "transfer", + CircuitType.Batch => "batch", + CircuitType.Deposit => "deposit", + CircuitType.Withdraw => "withdraw", + _ => "transfer" + }; + + public static ProofSystemInfo GetInfo(this ProofSystem system) => system switch + { + ProofSystem.Groth16 => new ProofSystemInfo("Groth16", 192, 10, true, false), + ProofSystem.Plonk => new ProofSystemInfo("PLONK", 512, 15, true, true), + ProofSystem.Stark => new ProofSystemInfo("STARK", 50000, 30, false, true), + _ => throw new ArgumentOutOfRangeException(nameof(system)) + }; +} + +/// Constants +public static class ZkConstants +{ + public const int MaxBatchSize = 1000; + public const int StateTreeDepth = 32; + public const int ProofExpiryBlocks = 100; +} diff --git a/sdk/flutter/lib/src/zk/client.dart b/sdk/flutter/lib/src/zk/client.dart new file mode 100644 index 0000000..55f1a02 --- /dev/null +++ b/sdk/flutter/lib/src/zk/client.dart @@ -0,0 +1,415 @@ +/// Synor ZK SDK Client for Flutter/Dart +/// +/// Zero-Knowledge proof systems for ZK-Rollups and privacy. +library synor_zk_client; + +import 'dart:convert'; +import 'dart:typed_data'; +import 'package:http/http.dart' as http; +import 'types.dart'; + +export 'types.dart'; + +/// Synor ZK SDK Client +/// +/// Provides zero-knowledge proof generation and verification for ZK-Rollups. +/// +/// Example: +/// ```dart +/// final zk = SynorZk(ZkConfig(apiKey: 'your-api-key')); +/// +/// // Generate a proof +/// final proof = await zk.proofs.generate(ProofRequest( +/// circuitType: CircuitType.transfer, +/// publicInputs: [...], +/// privateInputs: [...], +/// )); +/// +/// // Verify the proof +/// final result = await zk.proofs.verify(proof); +/// print('Valid: ${result.valid}'); +/// ``` +class SynorZk { + final ZkConfig config; + final http.Client _client; + bool _closed = false; + + late final ProofsClient proofs; + late final CircuitsClient circuits; + late final RollupClient rollup; + late final StateClient state; + late final CeremonyClient ceremony; + + SynorZk(this.config, {http.Client? client}) + : _client = client ?? http.Client() { + proofs = ProofsClient(this); + circuits = CircuitsClient(this); + rollup = RollupClient(this); + state = StateClient(this); + ceremony = CeremonyClient(this); + } + + /// Get the default proof system + ProofSystem get defaultProofSystem => config.defaultProofSystem; + + /// Health check + Future healthCheck() async { + try { + final result = await _get('/health'); + return result['status'] == 'healthy'; + } catch (_) { + return false; + } + } + + /// Get service info + Future> getInfo() async { + return _get('/info'); + } + + /// Close the client + void close() { + _closed = true; + _client.close(); + } + + /// Check if client is closed + bool get isClosed => _closed; + + // Internal HTTP methods + Future> _get(String path) async { + return _request('GET', path); + } + + Future> _post(String path, [Map? body]) async { + return _request('POST', path, body); + } + + Future> _delete(String path) async { + return _request('DELETE', path); + } + + Future> _request( + String method, + String path, [ + Map? body, + ]) async { + if (_closed) { + throw const ZkError('Client has been closed', code: 'CLIENT_CLOSED'); + } + + final url = Uri.parse('${config.endpoint}$path'); + final headers = { + 'Content-Type': 'application/json', + 'Authorization': 'Bearer ${config.apiKey}', + 'X-SDK-Version': 'flutter/0.1.0', + }; + + Exception? lastError; + + for (var attempt = 0; attempt <= config.retries; attempt++) { + try { + http.Response response; + switch (method) { + case 'GET': + response = await _client + .get(url, headers: headers) + .timeout(config.timeout); + break; + case 'POST': + response = await _client + .post(url, headers: headers, body: body != null ? jsonEncode(body) : null) + .timeout(config.timeout); + break; + case 'DELETE': + response = await _client + .delete(url, headers: headers) + .timeout(config.timeout); + break; + default: + throw ArgumentError('Unknown method: $method'); + } + + if (response.statusCode >= 400) { + final error = response.body.isNotEmpty + ? jsonDecode(response.body) as Map + : {}; + throw ZkError( + error['message'] as String? ?? 'HTTP ${response.statusCode}', + code: error['code'] as String?, + status: response.statusCode, + ); + } + + return jsonDecode(response.body) as Map; + } on ZkError { + rethrow; + } catch (e) { + lastError = e as Exception; + if (attempt < config.retries) { + await Future.delayed(Duration(milliseconds: 100 * (1 << attempt))); + } + } + } + + throw ZkError( + lastError?.toString() ?? 'Request failed', + code: 'NETWORK_ERROR', + ); + } +} + +/// Proofs sub-client +class ProofsClient { + final SynorZk _zk; + + ProofsClient(this._zk); + + /// Generate a zero-knowledge proof + Future generate(ProofRequest request) async { + final body = request.toJson(); + body['system'] ??= _zk.defaultProofSystem.value; + final result = await _zk._post('/proofs/generate', body); + return Proof.fromJson(result); + } + + /// Verify a proof + Future verify(Proof proof) async { + final result = await _zk._post('/proofs/verify', { + 'proof_id': proof.id, + 'data': proof.data, + 'public_inputs': proof.publicInputs, + 'system': proof.system.value, + 'circuit_type': proof.circuitType.value, + }); + return VerificationResult.fromJson(result); + } + + /// Get a proof by ID + Future get(String proofId) async { + final result = await _zk._get('/proofs/$proofId'); + return Proof.fromJson(result); + } + + /// List recent proofs + Future> list({int? limit, int? offset}) async { + var path = '/proofs'; + final params = []; + if (limit != null) params.add('limit=$limit'); + if (offset != null) params.add('offset=$offset'); + if (params.isNotEmpty) path += '?${params.join('&')}'; + + final result = await _zk._get(path); + final proofs = result['proofs'] as List?; + return proofs?.map((p) => Proof.fromJson(p as Map)).toList() ?? []; + } + + /// Serialize a proof to bytes + Future serialize(Proof proof) async { + final result = await _zk._post('/proofs/serialize', {'proof_id': proof.id}); + return base64Decode(result['data'] as String); + } + + /// Deserialize bytes to a proof + Future deserialize(Uint8List data, ProofSystem system) async { + final result = await _zk._post('/proofs/deserialize', { + 'data': base64Encode(data), + 'system': system.value, + }); + return Proof.fromJson(result); + } +} + +/// Circuits sub-client +class CircuitsClient { + final SynorZk _zk; + + CircuitsClient(this._zk); + + /// Get verification key for a circuit + Future getVerificationKey( + CircuitType circuitType, [ + ProofSystem? system, + ]) async { + final sys = system ?? _zk.defaultProofSystem; + final result = await _zk._get('/circuits/${circuitType.value}/vk?system=${sys.value}'); + return VerificationKey.fromJson(result); + } + + /// Get proving key for a circuit + Future getProvingKey( + CircuitType circuitType, [ + ProofSystem? system, + ]) async { + final sys = system ?? _zk.defaultProofSystem; + final result = await _zk._get('/circuits/${circuitType.value}/pk?system=${sys.value}'); + return ProvingKey.fromJson(result); + } + + /// List available circuits + Future> list() async { + final result = await _zk._get('/circuits'); + final circuits = result['circuits'] as List?; + return circuits?.map((c) => CircuitConfig.fromJson(c as Map)).toList() ?? []; + } + + /// Get circuit configuration + Future getConfig(CircuitType circuitType) async { + final result = await _zk._get('/circuits/${circuitType.value}/config'); + return CircuitConfig.fromJson(result); + } + + /// Compile a custom circuit + Future compile(CircuitConfig config) async { + final result = await _zk._post('/circuits/compile', config.toJson()); + return result['circuit_id'] as String; + } +} + +/// Rollup sub-client +class RollupClient { + final SynorZk _zk; + + RollupClient(this._zk); + + /// Get rollup statistics + Future getStats() async { + final result = await _zk._get('/rollup/stats'); + return RollupStats.fromJson(result); + } + + /// Get current batch + Future getCurrentBatch() async { + final result = await _zk._get('/rollup/batch/current'); + return Batch.fromJson(result); + } + + /// Get batch by number + Future getBatch(int batchNumber) async { + final result = await _zk._get('/rollup/batch/$batchNumber'); + return Batch.fromJson(result); + } + + /// Submit a transfer + Future submitTransfer(Transfer transfer) async { + final result = await _zk._post('/rollup/transfer', transfer.toJson()); + return result['tx_id'] as String; + } + + /// Submit a deposit + Future submitDeposit(Deposit deposit) async { + final result = await _zk._post('/rollup/deposit', deposit.toJson()); + return result['tx_id'] as String; + } + + /// Submit a withdrawal + Future submitWithdrawal(Withdrawal withdrawal) async { + final result = await _zk._post('/rollup/withdraw', withdrawal.toJson()); + return result['tx_id'] as String; + } + + /// Finalize current batch + Future finalizeBatch() async { + final result = await _zk._post('/rollup/batch/finalize', {}); + return Batch.fromJson(result); + } + + /// Get pending transactions + Future> getPendingTransactions() async { + final result = await _zk._get('/rollup/pending'); + final transactions = result['transactions'] as List?; + return transactions?.map((t) => Transfer.fromJson(t as Map)).toList() ?? []; + } +} + +/// State sub-client +class StateClient { + final SynorZk _zk; + + StateClient(this._zk); + + /// Get current state root + Future getRoot() async { + final result = await _zk._get('/state/root'); + return result['root'] as String; + } + + /// Get account state + Future getAccount(String address) async { + final result = await _zk._get('/state/account/$address'); + return AccountState.fromJson(result); + } + + /// Get merkle proof for account inclusion + Future getMerkleProof(String address) async { + final result = await _zk._get('/state/proof/$address'); + return MerkleProof.fromJson(result); + } + + /// Verify merkle proof + Future verifyMerkleProof(MerkleProof proof) async { + final result = await _zk._post('/state/proof/verify', proof.toJson()); + return result['valid'] as bool; + } + + /// Get state at specific batch + Future> getStateAtBatch(int batchNumber) async { + return _zk._get('/state/batch/$batchNumber'); + } +} + +/// Ceremony sub-client (trusted setup) +class CeremonyClient { + final SynorZk _zk; + + CeremonyClient(this._zk); + + /// Get ceremony status + Future getStatus( + CircuitType circuitType, [ + ProofSystem? system, + ]) async { + final sys = system ?? _zk.defaultProofSystem; + final result = await _zk._get('/ceremony/${circuitType.value}?system=${sys.value}'); + return TrustedSetup.fromJson(result); + } + + /// Contribute to ceremony + Future contribute( + CircuitType circuitType, + Uint8List entropy, [ + ProofSystem? system, + ]) async { + final sys = system ?? _zk.defaultProofSystem; + final result = await _zk._post('/ceremony/contribute', { + 'circuit_type': circuitType.value, + 'entropy': base64Encode(entropy), + 'system': sys.value, + }); + return CeremonyContribution.fromJson(result); + } + + /// Verify a contribution + Future verifyContribution( + CircuitType circuitType, + String contributionHash, + ) async { + final result = await _zk._post('/ceremony/verify', { + 'circuit_type': circuitType.value, + 'contribution_hash': contributionHash, + }); + return result['valid'] as bool; + } + + /// List contributions + Future> listContributions( + CircuitType circuitType, + ) async { + final result = await _zk._get('/ceremony/${circuitType.value}/contributions'); + final contributions = result['contributions'] as List?; + return contributions + ?.map((c) => CeremonyContribution.fromJson(c as Map)) + .toList() ?? + []; + } +} diff --git a/sdk/flutter/lib/src/zk/types.dart b/sdk/flutter/lib/src/zk/types.dart new file mode 100644 index 0000000..8d5309a --- /dev/null +++ b/sdk/flutter/lib/src/zk/types.dart @@ -0,0 +1,682 @@ +/// Synor ZK SDK Types for Flutter/Dart +/// +/// Zero-Knowledge proof systems for ZK-Rollups and privacy. +library synor_zk_types; + +import 'dart:convert'; +import 'dart:typed_data'; + +/// Proof system backends +enum ProofSystem { + /// Groth16 - smallest proofs (~192 bytes), fastest verification + groth16, + /// PLONK - universal trusted setup, medium proofs (~512 bytes) + plonk, + /// STARK - no trusted setup, largest proofs (~50KB) + stark, +} + +extension ProofSystemExtension on ProofSystem { + String get value { + switch (this) { + case ProofSystem.groth16: + return 'groth16'; + case ProofSystem.plonk: + return 'plonk'; + case ProofSystem.stark: + return 'stark'; + } + } + + static ProofSystem fromString(String value) { + switch (value) { + case 'groth16': + return ProofSystem.groth16; + case 'plonk': + return ProofSystem.plonk; + case 'stark': + return ProofSystem.stark; + default: + return ProofSystem.groth16; + } + } +} + +/// Circuit types for different operations +enum CircuitType { + /// Single transfer between accounts + transfer, + /// Batch of multiple transfers + batch, + /// Deposit from L1 to L2 + deposit, + /// Withdrawal from L2 to L1 + withdraw, +} + +extension CircuitTypeExtension on CircuitType { + String get value { + switch (this) { + case CircuitType.transfer: + return 'transfer'; + case CircuitType.batch: + return 'batch'; + case CircuitType.deposit: + return 'deposit'; + case CircuitType.withdraw: + return 'withdraw'; + } + } + + static CircuitType fromString(String value) { + switch (value) { + case 'transfer': + return CircuitType.transfer; + case 'batch': + return CircuitType.batch; + case 'deposit': + return CircuitType.deposit; + case 'withdraw': + return CircuitType.withdraw; + default: + return CircuitType.transfer; + } + } +} + +/// Rollup state +enum RollupState { + active, + paused, + finalizing, + finalized, +} + +/// Proof status +enum ProofStatus { + generating, + completed, + failed, + verified, +} + +/// Zero-knowledge proof +class Proof { + final String id; + final ProofSystem system; + final CircuitType circuitType; + final String data; // Base64 encoded + final List publicInputs; + final int size; + final int generationTimeMs; + final int createdAt; + + const Proof({ + required this.id, + required this.system, + required this.circuitType, + required this.data, + required this.publicInputs, + required this.size, + required this.generationTimeMs, + required this.createdAt, + }); + + factory Proof.fromJson(Map json) { + return Proof( + id: json['id'] as String, + system: ProofSystemExtension.fromString(json['system'] as String), + circuitType: CircuitTypeExtension.fromString(json['circuit_type'] as String), + data: json['data'] as String, + publicInputs: List.from(json['public_inputs'] ?? []), + size: json['size'] as int? ?? 0, + generationTimeMs: json['generation_time_ms'] as int? ?? 0, + createdAt: json['created_at'] as int? ?? 0, + ); + } + + Map toJson() => { + 'id': id, + 'system': system.value, + 'circuit_type': circuitType.value, + 'data': data, + 'public_inputs': publicInputs, + 'size': size, + 'generation_time_ms': generationTimeMs, + 'created_at': createdAt, + }; +} + +/// Verification key for a circuit +class VerificationKey { + final String circuitId; + final CircuitType circuitType; + final ProofSystem system; + final String data; // Base64 encoded + final int size; + + const VerificationKey({ + required this.circuitId, + required this.circuitType, + required this.system, + required this.data, + required this.size, + }); + + factory VerificationKey.fromJson(Map json) { + return VerificationKey( + circuitId: json['circuit_id'] as String, + circuitType: CircuitTypeExtension.fromString(json['circuit_type'] as String), + system: ProofSystemExtension.fromString(json['system'] as String), + data: json['data'] as String, + size: json['size'] as int? ?? 0, + ); + } +} + +/// Proving key for generating proofs +class ProvingKey { + final String circuitId; + final CircuitType circuitType; + final ProofSystem system; + final String data; // Base64 encoded + final int size; + + const ProvingKey({ + required this.circuitId, + required this.circuitType, + required this.system, + required this.data, + required this.size, + }); + + factory ProvingKey.fromJson(Map json) { + return ProvingKey( + circuitId: json['circuit_id'] as String, + circuitType: CircuitTypeExtension.fromString(json['circuit_type'] as String), + system: ProofSystemExtension.fromString(json['system'] as String), + data: json['data'] as String, + size: json['size'] as int? ?? 0, + ); + } +} + +/// Circuit configuration +class CircuitConfig { + final CircuitType type; + final int? maxBatchSize; + final int? treeDepth; + final bool? verifySignatures; + final Map? customConstraints; + + const CircuitConfig({ + required this.type, + this.maxBatchSize, + this.treeDepth, + this.verifySignatures, + this.customConstraints, + }); + + factory CircuitConfig.fromJson(Map json) { + return CircuitConfig( + type: CircuitTypeExtension.fromString(json['type'] as String), + maxBatchSize: json['max_batch_size'] as int?, + treeDepth: json['tree_depth'] as int?, + verifySignatures: json['verify_signatures'] as bool?, + customConstraints: json['custom_constraints'] as Map?, + ); + } + + Map toJson() { + final result = {'type': type.value}; + if (maxBatchSize != null) result['max_batch_size'] = maxBatchSize; + if (treeDepth != null) result['tree_depth'] = treeDepth; + if (verifySignatures != null) result['verify_signatures'] = verifySignatures; + if (customConstraints != null) result['custom_constraints'] = customConstraints; + return result; + } +} + +/// Account state in the rollup +class AccountState { + final String address; + final String balance; + final int nonce; + final String pubkeyHash; + + const AccountState({ + required this.address, + required this.balance, + required this.nonce, + required this.pubkeyHash, + }); + + factory AccountState.fromJson(Map json) { + return AccountState( + address: json['address'] as String, + balance: json['balance'] as String, + nonce: json['nonce'] as int? ?? 0, + pubkeyHash: json['pubkey_hash'] as String? ?? '', + ); + } +} + +/// Transfer operation +class Transfer { + final String from; + final String to; + final String amount; + final String fee; + final int nonce; + final String? signature; + + const Transfer({ + required this.from, + required this.to, + required this.amount, + required this.fee, + required this.nonce, + this.signature, + }); + + factory Transfer.fromJson(Map json) { + return Transfer( + from: json['from'] as String, + to: json['to'] as String, + amount: json['amount'] as String, + fee: json['fee'] as String, + nonce: json['nonce'] as int? ?? 0, + signature: json['signature'] as String?, + ); + } + + Map toJson() { + final result = { + 'from': from, + 'to': to, + 'amount': amount, + 'fee': fee, + 'nonce': nonce, + }; + if (signature != null) result['signature'] = signature; + return result; + } +} + +/// Deposit operation (L1 -> L2) +class Deposit { + final String l1TxHash; + final String recipient; + final String amount; + final String? token; + + const Deposit({ + required this.l1TxHash, + required this.recipient, + required this.amount, + this.token, + }); + + factory Deposit.fromJson(Map json) { + return Deposit( + l1TxHash: json['l1_tx_hash'] as String, + recipient: json['recipient'] as String, + amount: json['amount'] as String, + token: json['token'] as String?, + ); + } + + Map toJson() { + final result = { + 'l1_tx_hash': l1TxHash, + 'recipient': recipient, + 'amount': amount, + }; + if (token != null) result['token'] = token; + return result; + } +} + +/// Withdrawal operation (L2 -> L1) +class Withdrawal { + final String sender; + final String recipient; + final String amount; + final String? token; + final int nonce; + final String? signature; + + const Withdrawal({ + required this.sender, + required this.recipient, + required this.amount, + this.token, + required this.nonce, + this.signature, + }); + + factory Withdrawal.fromJson(Map json) { + return Withdrawal( + sender: json['sender'] as String, + recipient: json['recipient'] as String, + amount: json['amount'] as String, + token: json['token'] as String?, + nonce: json['nonce'] as int? ?? 0, + signature: json['signature'] as String?, + ); + } + + Map toJson() { + final result = { + 'sender': sender, + 'recipient': recipient, + 'amount': amount, + 'nonce': nonce, + }; + if (token != null) result['token'] = token; + if (signature != null) result['signature'] = signature; + return result; + } +} + +/// Rollup batch +class Batch { + final int batchNumber; + final String prevStateRoot; + final String newStateRoot; + final List transfers; + final List deposits; + final List withdrawals; + final Proof? proof; + final int timestamp; + + const Batch({ + required this.batchNumber, + required this.prevStateRoot, + required this.newStateRoot, + required this.transfers, + required this.deposits, + required this.withdrawals, + this.proof, + required this.timestamp, + }); + + factory Batch.fromJson(Map json) { + return Batch( + batchNumber: json['batch_number'] as int, + prevStateRoot: json['prev_state_root'] as String, + newStateRoot: json['new_state_root'] as String, + transfers: (json['transfers'] as List?) + ?.map((t) => Transfer.fromJson(t as Map)) + .toList() ?? + [], + deposits: (json['deposits'] as List?) + ?.map((d) => Deposit.fromJson(d as Map)) + .toList() ?? + [], + withdrawals: (json['withdrawals'] as List?) + ?.map((w) => Withdrawal.fromJson(w as Map)) + .toList() ?? + [], + proof: json['proof'] != null + ? Proof.fromJson(json['proof'] as Map) + : null, + timestamp: json['timestamp'] as int? ?? 0, + ); + } +} + +/// Rollup statistics +class RollupStats { + final int currentBatch; + final int totalTransactions; + final int totalProofs; + final int avgProofTimeMs; + final String stateRoot; + final int accountCount; + final String tvl; + + const RollupStats({ + required this.currentBatch, + required this.totalTransactions, + required this.totalProofs, + required this.avgProofTimeMs, + required this.stateRoot, + required this.accountCount, + required this.tvl, + }); + + factory RollupStats.fromJson(Map json) { + return RollupStats( + currentBatch: json['current_batch'] as int, + totalTransactions: json['total_transactions'] as int? ?? 0, + totalProofs: json['total_proofs'] as int? ?? 0, + avgProofTimeMs: json['avg_proof_time_ms'] as int? ?? 0, + stateRoot: json['state_root'] as String, + accountCount: json['account_count'] as int? ?? 0, + tvl: json['tvl'] as String? ?? '0', + ); + } +} + +/// Proof generation request +class ProofRequest { + final CircuitType circuitType; + final List publicInputs; + final List privateInputs; + final ProofSystem? system; + + const ProofRequest({ + required this.circuitType, + required this.publicInputs, + required this.privateInputs, + this.system, + }); + + Map toJson() { + final result = { + 'circuit_type': circuitType.value, + 'public_inputs': publicInputs, + 'private_inputs': privateInputs, + }; + if (system != null) result['system'] = system!.value; + return result; + } +} + +/// Proof verification result +class VerificationResult { + final bool valid; + final int verificationTimeMs; + final String? error; + + const VerificationResult({ + required this.valid, + required this.verificationTimeMs, + this.error, + }); + + factory VerificationResult.fromJson(Map json) { + return VerificationResult( + valid: json['valid'] as bool, + verificationTimeMs: json['verification_time_ms'] as int? ?? 0, + error: json['error'] as String?, + ); + } +} + +/// Merkle proof for state inclusion +class MerkleProof { + final String leaf; + final List path; + final List indices; + final String root; + + const MerkleProof({ + required this.leaf, + required this.path, + required this.indices, + required this.root, + }); + + factory MerkleProof.fromJson(Map json) { + return MerkleProof( + leaf: json['leaf'] as String, + path: List.from(json['path']), + indices: List.from(json['indices']), + root: json['root'] as String, + ); + } + + Map toJson() => { + 'leaf': leaf, + 'path': path, + 'indices': indices, + 'root': root, + }; +} + +/// Trusted setup ceremony contribution +class CeremonyContribution { + final String contributorId; + final String hash; + final int timestamp; + final bool verified; + + const CeremonyContribution({ + required this.contributorId, + required this.hash, + required this.timestamp, + required this.verified, + }); + + factory CeremonyContribution.fromJson(Map json) { + return CeremonyContribution( + contributorId: json['contributor_id'] as String, + hash: json['hash'] as String, + timestamp: json['timestamp'] as int? ?? 0, + verified: json['verified'] as bool? ?? false, + ); + } +} + +/// Trusted setup ceremony +class TrustedSetup { + final String id; + final CircuitType circuitType; + final ProofSystem system; + final int contributionCount; + final String latestHash; + final bool complete; + final List contributions; + + const TrustedSetup({ + required this.id, + required this.circuitType, + required this.system, + required this.contributionCount, + required this.latestHash, + required this.complete, + required this.contributions, + }); + + factory TrustedSetup.fromJson(Map json) { + return TrustedSetup( + id: json['id'] as String, + circuitType: CircuitTypeExtension.fromString(json['circuit_type'] as String), + system: ProofSystemExtension.fromString(json['system'] as String), + contributionCount: json['contribution_count'] as int? ?? 0, + latestHash: json['latest_hash'] as String? ?? '', + complete: json['complete'] as bool? ?? false, + contributions: (json['contributions'] as List?) + ?.map((c) => CeremonyContribution.fromJson(c as Map)) + .toList() ?? + [], + ); + } +} + +/// ZK SDK configuration +class ZkConfig { + final String apiKey; + final String endpoint; + final String wsEndpoint; + final Duration timeout; + final int retries; + final ProofSystem defaultProofSystem; + final bool debug; + + const ZkConfig({ + required this.apiKey, + this.endpoint = 'https://zk.synor.io/v1', + this.wsEndpoint = 'wss://zk.synor.io/v1/ws', + this.timeout = const Duration(seconds: 60), + this.retries = 3, + this.defaultProofSystem = ProofSystem.groth16, + this.debug = false, + }); +} + +/// ZK SDK error +class ZkError implements Exception { + final String message; + final String? code; + final int? status; + + const ZkError(this.message, {this.code, this.status}); + + @override + String toString() => code != null ? 'ZkError: $message ($code)' : 'ZkError: $message'; +} + +/// Proof system characteristics +class ProofSystemInfo { + final String name; + final int proofSize; + final int verificationTimeMs; + final bool trustedSetup; + final bool universal; + + const ProofSystemInfo({ + required this.name, + required this.proofSize, + required this.verificationTimeMs, + required this.trustedSetup, + required this.universal, + }); +} + +/// Get proof system info +ProofSystemInfo getProofSystemInfo(ProofSystem system) { + switch (system) { + case ProofSystem.groth16: + return const ProofSystemInfo( + name: 'Groth16', + proofSize: 192, + verificationTimeMs: 10, + trustedSetup: true, + universal: false, + ); + case ProofSystem.plonk: + return const ProofSystemInfo( + name: 'PLONK', + proofSize: 512, + verificationTimeMs: 15, + trustedSetup: true, + universal: true, + ); + case ProofSystem.stark: + return const ProofSystemInfo( + name: 'STARK', + proofSize: 50000, + verificationTimeMs: 30, + trustedSetup: false, + universal: true, + ); + } +} + +/// Constants +const int maxBatchSize = 1000; +const int stateTreeDepth = 32; +const int proofExpiryBlocks = 100; diff --git a/sdk/go/zk/client.go b/sdk/go/zk/client.go new file mode 100644 index 0000000..7513a7d --- /dev/null +++ b/sdk/go/zk/client.go @@ -0,0 +1,496 @@ +// Package zk provides the Synor ZK SDK for Go. +package zk + +import ( + "bytes" + "context" + "encoding/base64" + "encoding/json" + "fmt" + "io" + "net/http" + "sync/atomic" + "time" +) + +// Client is the main ZK SDK client +type Client struct { + config Config + httpClient *http.Client + closed atomic.Bool + + Proofs *ProofsClient + Circuits *CircuitsClient + Rollup *RollupClient + State *StateClient + Ceremony *CeremonyClient +} + +// New creates a new ZK SDK client +func New(config Config) *Client { + c := &Client{ + config: config, + httpClient: &http.Client{ + Timeout: config.Timeout, + }, + } + + c.Proofs = &ProofsClient{client: c} + c.Circuits = &CircuitsClient{client: c} + c.Rollup = &RollupClient{client: c} + c.State = &StateClient{client: c} + c.Ceremony = &CeremonyClient{client: c} + + return c +} + +// DefaultProofSystem returns the default proof system +func (c *Client) DefaultProofSystem() ProofSystem { + return c.config.DefaultProofSystem +} + +// HealthCheck checks if the service is healthy +func (c *Client) HealthCheck(ctx context.Context) (bool, error) { + var result map[string]interface{} + if err := c.get(ctx, "/health", &result); err != nil { + return false, nil + } + return result["status"] == "healthy", nil +} + +// GetInfo returns service info +func (c *Client) GetInfo(ctx context.Context) (map[string]interface{}, error) { + var result map[string]interface{} + err := c.get(ctx, "/info", &result) + return result, err +} + +// Close closes the client +func (c *Client) Close() { + c.closed.Store(true) +} + +// IsClosed returns whether the client is closed +func (c *Client) IsClosed() bool { + return c.closed.Load() +} + +// Internal HTTP methods +func (c *Client) get(ctx context.Context, path string, result interface{}) error { + return c.request(ctx, "GET", path, nil, result) +} + +func (c *Client) post(ctx context.Context, path string, body, result interface{}) error { + return c.request(ctx, "POST", path, body, result) +} + +func (c *Client) delete(ctx context.Context, path string, result interface{}) error { + return c.request(ctx, "DELETE", path, nil, result) +} + +func (c *Client) request(ctx context.Context, method, path string, body, result interface{}) error { + if c.closed.Load() { + return &Error{Message: "Client has been closed", Code: "CLIENT_CLOSED"} + } + + url := c.config.Endpoint + path + + var bodyReader io.Reader + if body != nil { + data, err := json.Marshal(body) + if err != nil { + return err + } + bodyReader = bytes.NewReader(data) + } + + var lastErr error + for attempt := 0; attempt <= c.config.Retries; attempt++ { + req, err := http.NewRequestWithContext(ctx, method, url, bodyReader) + if err != nil { + return err + } + + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", "Bearer "+c.config.APIKey) + req.Header.Set("X-SDK-Version", "go/0.1.0") + + resp, err := c.httpClient.Do(req) + if err != nil { + lastErr = err + if attempt < c.config.Retries { + time.Sleep(time.Duration(100*(1<= 400 { + var errResp struct { + Message string `json:"message"` + Code string `json:"code"` + } + json.Unmarshal(respBody, &errResp) + return &Error{ + Message: errResp.Message, + Code: errResp.Code, + Status: resp.StatusCode, + } + } + + if result != nil { + return json.Unmarshal(respBody, result) + } + return nil + } + + return &Error{ + Message: fmt.Sprintf("request failed: %v", lastErr), + Code: "NETWORK_ERROR", + } +} + +// ProofsClient handles proof operations +type ProofsClient struct { + client *Client +} + +// Generate generates a zero-knowledge proof +func (p *ProofsClient) Generate(ctx context.Context, req ProofRequest) (*Proof, error) { + system := req.System + if system == nil { + s := p.client.DefaultProofSystem() + system = &s + } + + body := map[string]interface{}{ + "circuit_type": req.CircuitType, + "public_inputs": req.PublicInputs, + "private_inputs": req.PrivateInputs, + "system": *system, + } + + var result Proof + if err := p.client.post(ctx, "/proofs/generate", body, &result); err != nil { + return nil, err + } + return &result, nil +} + +// Verify verifies a proof +func (p *ProofsClient) Verify(ctx context.Context, proof *Proof) (*VerificationResult, error) { + body := map[string]interface{}{ + "proof_id": proof.ID, + "data": proof.Data, + "public_inputs": proof.PublicInputs, + "system": proof.System, + "circuit_type": proof.CircuitType, + } + + var result VerificationResult + if err := p.client.post(ctx, "/proofs/verify", body, &result); err != nil { + return nil, err + } + return &result, nil +} + +// Get retrieves a proof by ID +func (p *ProofsClient) Get(ctx context.Context, proofID string) (*Proof, error) { + var result Proof + if err := p.client.get(ctx, "/proofs/"+proofID, &result); err != nil { + return nil, err + } + return &result, nil +} + +// List lists recent proofs +func (p *ProofsClient) List(ctx context.Context, limit, offset int) ([]Proof, error) { + path := "/proofs" + if limit > 0 || offset > 0 { + path = fmt.Sprintf("/proofs?limit=%d&offset=%d", limit, offset) + } + + var result proofsResponse + if err := p.client.get(ctx, path, &result); err != nil { + return nil, err + } + return result.Proofs, nil +} + +// Serialize serializes a proof to bytes +func (p *ProofsClient) Serialize(ctx context.Context, proof *Proof) ([]byte, error) { + var result serializeResponse + if err := p.client.post(ctx, "/proofs/serialize", map[string]string{"proof_id": proof.ID}, &result); err != nil { + return nil, err + } + return base64.StdEncoding.DecodeString(result.Data) +} + +// Deserialize deserializes bytes to a proof +func (p *ProofsClient) Deserialize(ctx context.Context, data []byte, system ProofSystem) (*Proof, error) { + body := map[string]interface{}{ + "data": base64.StdEncoding.EncodeToString(data), + "system": system, + } + + var result Proof + if err := p.client.post(ctx, "/proofs/deserialize", body, &result); err != nil { + return nil, err + } + return &result, nil +} + +// CircuitsClient handles circuit operations +type CircuitsClient struct { + client *Client +} + +// GetVerificationKey gets the verification key for a circuit +func (c *CircuitsClient) GetVerificationKey(ctx context.Context, circuitType CircuitType, system *ProofSystem) (*VerificationKey, error) { + sys := c.client.DefaultProofSystem() + if system != nil { + sys = *system + } + + var result VerificationKey + if err := c.client.get(ctx, fmt.Sprintf("/circuits/%s/vk?system=%s", circuitType, sys), &result); err != nil { + return nil, err + } + return &result, nil +} + +// GetProvingKey gets the proving key for a circuit +func (c *CircuitsClient) GetProvingKey(ctx context.Context, circuitType CircuitType, system *ProofSystem) (*ProvingKey, error) { + sys := c.client.DefaultProofSystem() + if system != nil { + sys = *system + } + + var result ProvingKey + if err := c.client.get(ctx, fmt.Sprintf("/circuits/%s/pk?system=%s", circuitType, sys), &result); err != nil { + return nil, err + } + return &result, nil +} + +// List lists available circuits +func (c *CircuitsClient) List(ctx context.Context) ([]CircuitConfig, error) { + var result circuitsResponse + if err := c.client.get(ctx, "/circuits", &result); err != nil { + return nil, err + } + return result.Circuits, nil +} + +// GetConfig gets circuit configuration +func (c *CircuitsClient) GetConfig(ctx context.Context, circuitType CircuitType) (*CircuitConfig, error) { + var result CircuitConfig + if err := c.client.get(ctx, fmt.Sprintf("/circuits/%s/config", circuitType), &result); err != nil { + return nil, err + } + return &result, nil +} + +// Compile compiles a custom circuit +func (c *CircuitsClient) Compile(ctx context.Context, config CircuitConfig) (string, error) { + var result circuitIDResponse + if err := c.client.post(ctx, "/circuits/compile", config, &result); err != nil { + return "", err + } + return result.CircuitID, nil +} + +// RollupClient handles rollup operations +type RollupClient struct { + client *Client +} + +// GetStats gets rollup statistics +func (r *RollupClient) GetStats(ctx context.Context) (*RollupStats, error) { + var result RollupStats + if err := r.client.get(ctx, "/rollup/stats", &result); err != nil { + return nil, err + } + return &result, nil +} + +// GetCurrentBatch gets the current batch +func (r *RollupClient) GetCurrentBatch(ctx context.Context) (*Batch, error) { + var result Batch + if err := r.client.get(ctx, "/rollup/batch/current", &result); err != nil { + return nil, err + } + return &result, nil +} + +// GetBatch gets a batch by number +func (r *RollupClient) GetBatch(ctx context.Context, batchNumber uint64) (*Batch, error) { + var result Batch + if err := r.client.get(ctx, fmt.Sprintf("/rollup/batch/%d", batchNumber), &result); err != nil { + return nil, err + } + return &result, nil +} + +// SubmitTransfer submits a transfer +func (r *RollupClient) SubmitTransfer(ctx context.Context, transfer Transfer) (string, error) { + var result txIDResponse + if err := r.client.post(ctx, "/rollup/transfer", transfer, &result); err != nil { + return "", err + } + return result.TxID, nil +} + +// SubmitDeposit submits a deposit +func (r *RollupClient) SubmitDeposit(ctx context.Context, deposit Deposit) (string, error) { + var result txIDResponse + if err := r.client.post(ctx, "/rollup/deposit", deposit, &result); err != nil { + return "", err + } + return result.TxID, nil +} + +// SubmitWithdrawal submits a withdrawal +func (r *RollupClient) SubmitWithdrawal(ctx context.Context, withdrawal Withdrawal) (string, error) { + var result txIDResponse + if err := r.client.post(ctx, "/rollup/withdraw", withdrawal, &result); err != nil { + return "", err + } + return result.TxID, nil +} + +// FinalizeBatch finalizes the current batch +func (r *RollupClient) FinalizeBatch(ctx context.Context) (*Batch, error) { + var result Batch + if err := r.client.post(ctx, "/rollup/batch/finalize", struct{}{}, &result); err != nil { + return nil, err + } + return &result, nil +} + +// GetPendingTransactions gets pending transactions +func (r *RollupClient) GetPendingTransactions(ctx context.Context) ([]Transfer, error) { + var result transactionsResponse + if err := r.client.get(ctx, "/rollup/pending", &result); err != nil { + return nil, err + } + return result.Transactions, nil +} + +// StateClient handles state operations +type StateClient struct { + client *Client +} + +// GetRoot gets the current state root +func (s *StateClient) GetRoot(ctx context.Context) (string, error) { + var result rootResponse + if err := s.client.get(ctx, "/state/root", &result); err != nil { + return "", err + } + return result.Root, nil +} + +// GetAccount gets account state +func (s *StateClient) GetAccount(ctx context.Context, address string) (*AccountState, error) { + var result AccountState + if err := s.client.get(ctx, "/state/account/"+address, &result); err != nil { + return nil, err + } + return &result, nil +} + +// GetMerkleProof gets merkle proof for account inclusion +func (s *StateClient) GetMerkleProof(ctx context.Context, address string) (*MerkleProof, error) { + var result MerkleProof + if err := s.client.get(ctx, "/state/proof/"+address, &result); err != nil { + return nil, err + } + return &result, nil +} + +// VerifyMerkleProof verifies a merkle proof +func (s *StateClient) VerifyMerkleProof(ctx context.Context, proof MerkleProof) (bool, error) { + var result validResponse + if err := s.client.post(ctx, "/state/proof/verify", proof, &result); err != nil { + return false, err + } + return result.Valid, nil +} + +// GetStateAtBatch gets state at specific batch +func (s *StateClient) GetStateAtBatch(ctx context.Context, batchNumber uint64) (map[string]interface{}, error) { + var result map[string]interface{} + if err := s.client.get(ctx, fmt.Sprintf("/state/batch/%d", batchNumber), &result); err != nil { + return nil, err + } + return result, nil +} + +// CeremonyClient handles ceremony operations +type CeremonyClient struct { + client *Client +} + +// GetStatus gets ceremony status +func (c *CeremonyClient) GetStatus(ctx context.Context, circuitType CircuitType, system *ProofSystem) (*TrustedSetup, error) { + sys := c.client.DefaultProofSystem() + if system != nil { + sys = *system + } + + var result TrustedSetup + if err := c.client.get(ctx, fmt.Sprintf("/ceremony/%s?system=%s", circuitType, sys), &result); err != nil { + return nil, err + } + return &result, nil +} + +// Contribute contributes to ceremony +func (c *CeremonyClient) Contribute(ctx context.Context, circuitType CircuitType, entropy []byte, system *ProofSystem) (*CeremonyContribution, error) { + sys := c.client.DefaultProofSystem() + if system != nil { + sys = *system + } + + body := map[string]interface{}{ + "circuit_type": circuitType, + "entropy": base64.StdEncoding.EncodeToString(entropy), + "system": sys, + } + + var result CeremonyContribution + if err := c.client.post(ctx, "/ceremony/contribute", body, &result); err != nil { + return nil, err + } + return &result, nil +} + +// VerifyContribution verifies a contribution +func (c *CeremonyClient) VerifyContribution(ctx context.Context, circuitType CircuitType, contributionHash string) (bool, error) { + body := map[string]interface{}{ + "circuit_type": circuitType, + "contribution_hash": contributionHash, + } + + var result validResponse + if err := c.client.post(ctx, "/ceremony/verify", body, &result); err != nil { + return false, err + } + return result.Valid, nil +} + +// ListContributions lists contributions +func (c *CeremonyClient) ListContributions(ctx context.Context, circuitType CircuitType) ([]CeremonyContribution, error) { + var result contributionsResponse + if err := c.client.get(ctx, fmt.Sprintf("/ceremony/%s/contributions", circuitType), &result); err != nil { + return nil, err + } + return result.Contributions, nil +} diff --git a/sdk/go/zk/types.go b/sdk/go/zk/types.go new file mode 100644 index 0000000..a47d24b --- /dev/null +++ b/sdk/go/zk/types.go @@ -0,0 +1,323 @@ +// Package zk provides the Synor ZK SDK for Go. +// +// Zero-Knowledge proof systems for ZK-Rollups and privacy. +package zk + +import ( + "encoding/json" + "fmt" + "time" +) + +// ProofSystem represents available proof system backends +type ProofSystem string + +const ( + // Groth16 - smallest proofs (~192 bytes), fastest verification + ProofSystemGroth16 ProofSystem = "groth16" + // Plonk - universal trusted setup, medium proofs (~512 bytes) + ProofSystemPlonk ProofSystem = "plonk" + // Stark - no trusted setup, largest proofs (~50KB) + ProofSystemStark ProofSystem = "stark" +) + +// CircuitType represents the type of circuit +type CircuitType string + +const ( + // CircuitTransfer - single transfer between accounts + CircuitTransfer CircuitType = "transfer" + // CircuitBatch - batch of multiple transfers + CircuitBatch CircuitType = "batch" + // CircuitDeposit - deposit from L1 to L2 + CircuitDeposit CircuitType = "deposit" + // CircuitWithdraw - withdrawal from L2 to L1 + CircuitWithdraw CircuitType = "withdraw" +) + +// RollupState represents the state of a rollup +type RollupState string + +const ( + RollupStateActive RollupState = "active" + RollupStatePaused RollupState = "paused" + RollupStateFinalizing RollupState = "finalizing" + RollupStateFinalized RollupState = "finalized" +) + +// ProofStatus represents the status of a proof +type ProofStatus string + +const ( + ProofStatusGenerating ProofStatus = "generating" + ProofStatusCompleted ProofStatus = "completed" + ProofStatusFailed ProofStatus = "failed" + ProofStatusVerified ProofStatus = "verified" +) + +// Proof represents a zero-knowledge proof +type Proof struct { + ID string `json:"id"` + System ProofSystem `json:"system"` + CircuitType CircuitType `json:"circuit_type"` + Data string `json:"data"` // Base64 encoded + PublicInputs []string `json:"public_inputs"` + Size int `json:"size"` + GenerationTimeMs int64 `json:"generation_time_ms"` + CreatedAt int64 `json:"created_at"` +} + +// VerificationKey represents a verification key for a circuit +type VerificationKey struct { + CircuitID string `json:"circuit_id"` + CircuitType CircuitType `json:"circuit_type"` + System ProofSystem `json:"system"` + Data string `json:"data"` // Base64 encoded + Size int `json:"size"` +} + +// ProvingKey represents a proving key for generating proofs +type ProvingKey struct { + CircuitID string `json:"circuit_id"` + CircuitType CircuitType `json:"circuit_type"` + System ProofSystem `json:"system"` + Data string `json:"data"` // Base64 encoded + Size int `json:"size"` +} + +// CircuitConfig represents circuit configuration +type CircuitConfig struct { + Type CircuitType `json:"type"` + MaxBatchSize *int `json:"max_batch_size,omitempty"` + TreeDepth *int `json:"tree_depth,omitempty"` + VerifySignatures *bool `json:"verify_signatures,omitempty"` + CustomConstraints map[string]interface{} `json:"custom_constraints,omitempty"` +} + +// AccountState represents account state in the rollup +type AccountState struct { + Address string `json:"address"` + Balance string `json:"balance"` + Nonce uint64 `json:"nonce"` + PubkeyHash string `json:"pubkey_hash"` +} + +// Transfer represents a transfer operation +type Transfer struct { + From string `json:"from"` + To string `json:"to"` + Amount string `json:"amount"` + Fee string `json:"fee"` + Nonce uint64 `json:"nonce"` + Signature *string `json:"signature,omitempty"` +} + +// Deposit represents a deposit operation (L1 -> L2) +type Deposit struct { + L1TxHash string `json:"l1_tx_hash"` + Recipient string `json:"recipient"` + Amount string `json:"amount"` + Token *string `json:"token,omitempty"` +} + +// Withdrawal represents a withdrawal operation (L2 -> L1) +type Withdrawal struct { + Sender string `json:"sender"` + Recipient string `json:"recipient"` + Amount string `json:"amount"` + Token *string `json:"token,omitempty"` + Nonce uint64 `json:"nonce"` + Signature *string `json:"signature,omitempty"` +} + +// Batch represents a rollup batch +type Batch struct { + BatchNumber uint64 `json:"batch_number"` + PrevStateRoot string `json:"prev_state_root"` + NewStateRoot string `json:"new_state_root"` + Transfers []Transfer `json:"transfers"` + Deposits []Deposit `json:"deposits"` + Withdrawals []Withdrawal `json:"withdrawals"` + Proof *Proof `json:"proof,omitempty"` + Timestamp int64 `json:"timestamp"` +} + +// RollupStats represents rollup statistics +type RollupStats struct { + CurrentBatch uint64 `json:"current_batch"` + TotalTransactions uint64 `json:"total_transactions"` + TotalProofs uint64 `json:"total_proofs"` + AvgProofTimeMs int64 `json:"avg_proof_time_ms"` + StateRoot string `json:"state_root"` + AccountCount uint64 `json:"account_count"` + TVL string `json:"tvl"` +} + +// ProofRequest represents a proof generation request +type ProofRequest struct { + CircuitType CircuitType `json:"circuit_type"` + PublicInputs []string `json:"public_inputs"` + PrivateInputs []string `json:"private_inputs"` + System *ProofSystem `json:"system,omitempty"` +} + +// VerificationResult represents proof verification result +type VerificationResult struct { + Valid bool `json:"valid"` + VerificationTimeMs int64 `json:"verification_time_ms"` + Error *string `json:"error,omitempty"` +} + +// MerkleProof represents a merkle proof for state inclusion +type MerkleProof struct { + Leaf string `json:"leaf"` + Path []string `json:"path"` + Indices []int `json:"indices"` + Root string `json:"root"` +} + +// CeremonyContribution represents a trusted setup ceremony contribution +type CeremonyContribution struct { + ContributorID string `json:"contributor_id"` + Hash string `json:"hash"` + Timestamp int64 `json:"timestamp"` + Verified bool `json:"verified"` +} + +// TrustedSetup represents a trusted setup ceremony +type TrustedSetup struct { + ID string `json:"id"` + CircuitType CircuitType `json:"circuit_type"` + System ProofSystem `json:"system"` + ContributionCount int `json:"contribution_count"` + LatestHash string `json:"latest_hash"` + Complete bool `json:"complete"` + Contributions []CeremonyContribution `json:"contributions"` +} + +// Config represents ZK SDK configuration +type Config struct { + APIKey string + Endpoint string + WSEndpoint string + Timeout time.Duration + Retries int + DefaultProofSystem ProofSystem + Debug bool +} + +// DefaultConfig returns the default configuration +func DefaultConfig(apiKey string) Config { + return Config{ + APIKey: apiKey, + Endpoint: "https://zk.synor.io/v1", + WSEndpoint: "wss://zk.synor.io/v1/ws", + Timeout: 60 * time.Second, + Retries: 3, + DefaultProofSystem: ProofSystemGroth16, + Debug: false, + } +} + +// Error represents a ZK SDK error +type Error struct { + Message string + Code string + Status int +} + +func (e *Error) Error() string { + if e.Code != "" { + return fmt.Sprintf("%s (%s)", e.Message, e.Code) + } + return e.Message +} + +// ProofSystemInfo contains information about a proof system +type ProofSystemInfo struct { + Name string + ProofSize int + VerificationTimeMs int + TrustedSetup bool + Universal bool +} + +// GetProofSystemInfo returns information about a proof system +func GetProofSystemInfo(system ProofSystem) ProofSystemInfo { + switch system { + case ProofSystemGroth16: + return ProofSystemInfo{ + Name: "Groth16", + ProofSize: 192, + VerificationTimeMs: 10, + TrustedSetup: true, + Universal: false, + } + case ProofSystemPlonk: + return ProofSystemInfo{ + Name: "PLONK", + ProofSize: 512, + VerificationTimeMs: 15, + TrustedSetup: true, + Universal: true, + } + case ProofSystemStark: + return ProofSystemInfo{ + Name: "STARK", + ProofSize: 50000, + VerificationTimeMs: 30, + TrustedSetup: false, + Universal: true, + } + default: + return ProofSystemInfo{} + } +} + +// Constants +const ( + MaxBatchSize = 1000 + StateTreeDepth = 32 + ProofExpiryBlocks = 100 +) + +// Helper for JSON responses +type jsonResponse struct { + Data json.RawMessage `json:"data,omitempty"` +} + +type proofsResponse struct { + Proofs []Proof `json:"proofs"` +} + +type circuitsResponse struct { + Circuits []CircuitConfig `json:"circuits"` +} + +type transactionsResponse struct { + Transactions []Transfer `json:"transactions"` +} + +type contributionsResponse struct { + Contributions []CeremonyContribution `json:"contributions"` +} + +type rootResponse struct { + Root string `json:"root"` +} + +type validResponse struct { + Valid bool `json:"valid"` +} + +type txIDResponse struct { + TxID string `json:"tx_id"` +} + +type circuitIDResponse struct { + CircuitID string `json:"circuit_id"` +} + +type serializeResponse struct { + Data string `json:"data"` +} diff --git a/sdk/java/src/main/java/io/synor/zk/SynorZk.java b/sdk/java/src/main/java/io/synor/zk/SynorZk.java new file mode 100644 index 0000000..19832dc --- /dev/null +++ b/sdk/java/src/main/java/io/synor/zk/SynorZk.java @@ -0,0 +1,321 @@ +package io.synor.zk; + +import io.synor.zk.Types.*; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.reflect.TypeToken; + +import java.io.IOException; +import java.lang.reflect.Type; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.time.Duration; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * Synor ZK SDK Client for Java + * + * Zero-Knowledge proof systems for ZK-Rollups and privacy. + */ +public class SynorZk { + private final ZkConfig config; + private final HttpClient httpClient; + private final Gson gson; + private final AtomicBoolean closed = new AtomicBoolean(false); + + public final ProofsClient proofs; + public final CircuitsClient circuits; + public final RollupClient rollup; + public final StateClient state; + public final CeremonyClient ceremony; + + public SynorZk(ZkConfig config) { + this.config = config; + this.httpClient = HttpClient.newBuilder() + .connectTimeout(Duration.ofMillis(config.getTimeout())) + .build(); + this.gson = new GsonBuilder().create(); + + this.proofs = new ProofsClient(this); + this.circuits = new CircuitsClient(this); + this.rollup = new RollupClient(this); + this.state = new StateClient(this); + this.ceremony = new CeremonyClient(this); + } + + public ProofSystem getDefaultProofSystem() { + return config.getDefaultProofSystem(); + } + + public CompletableFuture healthCheck() { + return get("/health", Map.class) + .thenApply(result -> "healthy".equals(result.get("status"))) + .exceptionally(e -> false); + } + + public CompletableFuture> getInfo() { + return get("/info", new TypeToken>(){}.getType()); + } + + public void close() { + closed.set(true); + } + + public boolean isClosed() { + return closed.get(); + } + + // Internal HTTP methods + CompletableFuture get(String path, Type type) { + return request("GET", path, null, type); + } + + CompletableFuture post(String path, Object body, Type type) { + return request("POST", path, body, type); + } + + private CompletableFuture request(String method, String path, Object body, Type type) { + if (closed.get()) { + return CompletableFuture.failedFuture( + new ZkException("Client has been closed", "CLIENT_CLOSED")); + } + + String url = config.getEndpoint() + path; + HttpRequest.Builder requestBuilder = HttpRequest.newBuilder() + .uri(URI.create(url)) + .header("Content-Type", "application/json") + .header("Authorization", "Bearer " + config.getApiKey()) + .header("X-SDK-Version", "java/0.1.0") + .timeout(Duration.ofMillis(config.getTimeout())); + + if ("GET".equals(method)) { + requestBuilder.GET(); + } else if ("POST".equals(method)) { + String jsonBody = body != null ? gson.toJson(body) : "{}"; + requestBuilder.POST(HttpRequest.BodyPublishers.ofString(jsonBody)); + } else if ("DELETE".equals(method)) { + requestBuilder.DELETE(); + } + + return httpClient.sendAsync(requestBuilder.build(), HttpResponse.BodyHandlers.ofString()) + .thenApply(response -> { + if (response.statusCode() >= 400) { + Map error = gson.fromJson(response.body(), Map.class); + throw new ZkException( + (String) error.getOrDefault("message", "HTTP " + response.statusCode()), + (String) error.get("code"), + response.statusCode() + ); + } + return gson.fromJson(response.body(), type); + }); + } + + /** Proofs sub-client */ + public static class ProofsClient { + private final SynorZk zk; + + ProofsClient(SynorZk zk) { + this.zk = zk; + } + + public CompletableFuture generate(ProofRequest request) { + Map body = new HashMap<>(); + body.put("circuit_type", request.getCircuitType().getValue()); + body.put("public_inputs", request.getPublicInputs()); + body.put("private_inputs", request.getPrivateInputs()); + body.put("system", (request.getSystem() != null ? request.getSystem() : zk.getDefaultProofSystem()).getValue()); + return zk.post("/proofs/generate", body, Proof.class); + } + + public CompletableFuture verify(Proof proof) { + Map body = new HashMap<>(); + body.put("proof_id", proof.getId()); + body.put("data", proof.getData()); + body.put("public_inputs", proof.getPublicInputs()); + body.put("system", proof.getSystem().getValue()); + body.put("circuit_type", proof.getCircuitType().getValue()); + return zk.post("/proofs/verify", body, VerificationResult.class); + } + + public CompletableFuture get(String proofId) { + return zk.get("/proofs/" + proofId, Proof.class); + } + + public CompletableFuture> list(Integer limit, Integer offset) { + StringBuilder path = new StringBuilder("/proofs"); + if (limit != null || offset != null) { + path.append("?"); + if (limit != null) path.append("limit=").append(limit); + if (limit != null && offset != null) path.append("&"); + if (offset != null) path.append("offset=").append(offset); + } + return zk.get(path.toString(), new TypeToken>>(){}.getType()) + .thenApply(result -> ((Map>) result).getOrDefault("proofs", Collections.emptyList())); + } + + public CompletableFuture serialize(Proof proof) { + Map body = new HashMap<>(); + body.put("proof_id", proof.getId()); + return zk.post("/proofs/serialize", body, new TypeToken>(){}.getType()) + .thenApply(result -> Base64.getDecoder().decode(((Map) result).get("data"))); + } + + public CompletableFuture deserialize(byte[] data, ProofSystem system) { + Map body = new HashMap<>(); + body.put("data", Base64.getEncoder().encodeToString(data)); + body.put("system", system.getValue()); + return zk.post("/proofs/deserialize", body, Proof.class); + } + } + + /** Circuits sub-client */ + public static class CircuitsClient { + private final SynorZk zk; + + CircuitsClient(SynorZk zk) { + this.zk = zk; + } + + public CompletableFuture getVerificationKey(CircuitType circuitType, ProofSystem system) { + ProofSystem sys = system != null ? system : zk.getDefaultProofSystem(); + return zk.get("/circuits/" + circuitType.getValue() + "/vk?system=" + sys.getValue(), VerificationKey.class); + } + + public CompletableFuture getProvingKey(CircuitType circuitType, ProofSystem system) { + ProofSystem sys = system != null ? system : zk.getDefaultProofSystem(); + return zk.get("/circuits/" + circuitType.getValue() + "/pk?system=" + sys.getValue(), ProvingKey.class); + } + + public CompletableFuture> list() { + return zk.get("/circuits", new TypeToken>>(){}.getType()) + .thenApply(result -> ((Map>) result).getOrDefault("circuits", Collections.emptyList())); + } + + public CompletableFuture getConfig(CircuitType circuitType) { + return zk.get("/circuits/" + circuitType.getValue() + "/config", CircuitConfig.class); + } + + public CompletableFuture compile(CircuitConfig config) { + return zk.post("/circuits/compile", config, new TypeToken>(){}.getType()) + .thenApply(result -> ((Map) result).get("circuit_id")); + } + } + + /** Rollup sub-client */ + public static class RollupClient { + private final SynorZk zk; + + RollupClient(SynorZk zk) { + this.zk = zk; + } + + public CompletableFuture getStats() { + return zk.get("/rollup/stats", RollupStats.class); + } + + public CompletableFuture getCurrentBatch() { + return zk.get("/rollup/batch/current", Batch.class); + } + + public CompletableFuture getBatch(long batchNumber) { + return zk.get("/rollup/batch/" + batchNumber, Batch.class); + } + + public CompletableFuture submitTransfer(Transfer transfer) { + return zk.post("/rollup/transfer", transfer, new TypeToken>(){}.getType()) + .thenApply(result -> ((Map) result).get("tx_id")); + } + + public CompletableFuture submitDeposit(Deposit deposit) { + return zk.post("/rollup/deposit", deposit, new TypeToken>(){}.getType()) + .thenApply(result -> ((Map) result).get("tx_id")); + } + + public CompletableFuture submitWithdrawal(Withdrawal withdrawal) { + return zk.post("/rollup/withdraw", withdrawal, new TypeToken>(){}.getType()) + .thenApply(result -> ((Map) result).get("tx_id")); + } + + public CompletableFuture finalizeBatch() { + return zk.post("/rollup/batch/finalize", Collections.emptyMap(), Batch.class); + } + + public CompletableFuture> getPendingTransactions() { + return zk.get("/rollup/pending", new TypeToken>>(){}.getType()) + .thenApply(result -> ((Map>) result).getOrDefault("transactions", Collections.emptyList())); + } + } + + /** State sub-client */ + public static class StateClient { + private final SynorZk zk; + + StateClient(SynorZk zk) { + this.zk = zk; + } + + public CompletableFuture getRoot() { + return zk.get("/state/root", new TypeToken>(){}.getType()) + .thenApply(result -> ((Map) result).get("root")); + } + + public CompletableFuture getAccount(String address) { + return zk.get("/state/account/" + address, AccountState.class); + } + + public CompletableFuture getMerkleProof(String address) { + return zk.get("/state/proof/" + address, MerkleProof.class); + } + + public CompletableFuture verifyMerkleProof(MerkleProof proof) { + return zk.post("/state/proof/verify", proof, new TypeToken>(){}.getType()) + .thenApply(result -> ((Map) result).get("valid")); + } + + public CompletableFuture> getStateAtBatch(long batchNumber) { + return zk.get("/state/batch/" + batchNumber, new TypeToken>(){}.getType()); + } + } + + /** Ceremony sub-client */ + public static class CeremonyClient { + private final SynorZk zk; + + CeremonyClient(SynorZk zk) { + this.zk = zk; + } + + public CompletableFuture getStatus(CircuitType circuitType, ProofSystem system) { + ProofSystem sys = system != null ? system : zk.getDefaultProofSystem(); + return zk.get("/ceremony/" + circuitType.getValue() + "?system=" + sys.getValue(), TrustedSetup.class); + } + + public CompletableFuture contribute(CircuitType circuitType, byte[] entropy, ProofSystem system) { + ProofSystem sys = system != null ? system : zk.getDefaultProofSystem(); + Map body = new HashMap<>(); + body.put("circuit_type", circuitType.getValue()); + body.put("entropy", Base64.getEncoder().encodeToString(entropy)); + body.put("system", sys.getValue()); + return zk.post("/ceremony/contribute", body, CeremonyContribution.class); + } + + public CompletableFuture verifyContribution(CircuitType circuitType, String contributionHash) { + Map body = new HashMap<>(); + body.put("circuit_type", circuitType.getValue()); + body.put("contribution_hash", contributionHash); + return zk.post("/ceremony/verify", body, new TypeToken>(){}.getType()) + .thenApply(result -> ((Map) result).get("valid")); + } + + public CompletableFuture> listContributions(CircuitType circuitType) { + return zk.get("/ceremony/" + circuitType.getValue() + "/contributions", new TypeToken>>(){}.getType()) + .thenApply(result -> ((Map>) result).getOrDefault("contributions", Collections.emptyList())); + } + } +} diff --git a/sdk/java/src/main/java/io/synor/zk/Types.java b/sdk/java/src/main/java/io/synor/zk/Types.java new file mode 100644 index 0000000..6c51cb8 --- /dev/null +++ b/sdk/java/src/main/java/io/synor/zk/Types.java @@ -0,0 +1,539 @@ +package io.synor.zk; + +import java.util.List; +import java.util.Map; + +/** + * Synor ZK SDK Types for Java + * + * Zero-Knowledge proof systems for ZK-Rollups and privacy. + */ +public class Types { + + /** Proof system backends */ + public enum ProofSystem { + /** Groth16 - smallest proofs (~192 bytes), fastest verification */ + GROTH16("groth16"), + /** PLONK - universal trusted setup, medium proofs (~512 bytes) */ + PLONK("plonk"), + /** STARK - no trusted setup, largest proofs (~50KB) */ + STARK("stark"); + + private final String value; + + ProofSystem(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + + public static ProofSystem fromString(String value) { + for (ProofSystem system : values()) { + if (system.value.equals(value)) { + return system; + } + } + return GROTH16; + } + } + + /** Circuit types for different operations */ + public enum CircuitType { + /** Single transfer between accounts */ + TRANSFER("transfer"), + /** Batch of multiple transfers */ + BATCH("batch"), + /** Deposit from L1 to L2 */ + DEPOSIT("deposit"), + /** Withdrawal from L2 to L1 */ + WITHDRAW("withdraw"); + + private final String value; + + CircuitType(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + + public static CircuitType fromString(String value) { + for (CircuitType type : values()) { + if (type.value.equals(value)) { + return type; + } + } + return TRANSFER; + } + } + + /** Rollup state */ + public enum RollupState { + ACTIVE, PAUSED, FINALIZING, FINALIZED + } + + /** Proof status */ + public enum ProofStatus { + GENERATING, COMPLETED, FAILED, VERIFIED + } + + /** Zero-knowledge proof */ + public static class Proof { + private String id; + private ProofSystem system; + private CircuitType circuitType; + private String data; + private List publicInputs; + private int size; + private long generationTimeMs; + private long createdAt; + + public Proof() {} + + public Proof(String id, ProofSystem system, CircuitType circuitType, String data, + List publicInputs, int size, long generationTimeMs, long createdAt) { + this.id = id; + this.system = system; + this.circuitType = circuitType; + this.data = data; + this.publicInputs = publicInputs; + this.size = size; + this.generationTimeMs = generationTimeMs; + this.createdAt = createdAt; + } + + public String getId() { return id; } + public void setId(String id) { this.id = id; } + public ProofSystem getSystem() { return system; } + public void setSystem(ProofSystem system) { this.system = system; } + public CircuitType getCircuitType() { return circuitType; } + public void setCircuitType(CircuitType circuitType) { this.circuitType = circuitType; } + public String getData() { return data; } + public void setData(String data) { this.data = data; } + public List getPublicInputs() { return publicInputs; } + public void setPublicInputs(List publicInputs) { this.publicInputs = publicInputs; } + public int getSize() { return size; } + public void setSize(int size) { this.size = size; } + public long getGenerationTimeMs() { return generationTimeMs; } + public void setGenerationTimeMs(long generationTimeMs) { this.generationTimeMs = generationTimeMs; } + public long getCreatedAt() { return createdAt; } + public void setCreatedAt(long createdAt) { this.createdAt = createdAt; } + } + + /** Verification key for a circuit */ + public static class VerificationKey { + private String circuitId; + private CircuitType circuitType; + private ProofSystem system; + private String data; + private int size; + + public String getCircuitId() { return circuitId; } + public void setCircuitId(String circuitId) { this.circuitId = circuitId; } + public CircuitType getCircuitType() { return circuitType; } + public void setCircuitType(CircuitType circuitType) { this.circuitType = circuitType; } + public ProofSystem getSystem() { return system; } + public void setSystem(ProofSystem system) { this.system = system; } + public String getData() { return data; } + public void setData(String data) { this.data = data; } + public int getSize() { return size; } + public void setSize(int size) { this.size = size; } + } + + /** Proving key for generating proofs */ + public static class ProvingKey { + private String circuitId; + private CircuitType circuitType; + private ProofSystem system; + private String data; + private int size; + + public String getCircuitId() { return circuitId; } + public void setCircuitId(String circuitId) { this.circuitId = circuitId; } + public CircuitType getCircuitType() { return circuitType; } + public void setCircuitType(CircuitType circuitType) { this.circuitType = circuitType; } + public ProofSystem getSystem() { return system; } + public void setSystem(ProofSystem system) { this.system = system; } + public String getData() { return data; } + public void setData(String data) { this.data = data; } + public int getSize() { return size; } + public void setSize(int size) { this.size = size; } + } + + /** Circuit configuration */ + public static class CircuitConfig { + private CircuitType type; + private Integer maxBatchSize; + private Integer treeDepth; + private Boolean verifySignatures; + private Map customConstraints; + + public CircuitType getType() { return type; } + public void setType(CircuitType type) { this.type = type; } + public Integer getMaxBatchSize() { return maxBatchSize; } + public void setMaxBatchSize(Integer maxBatchSize) { this.maxBatchSize = maxBatchSize; } + public Integer getTreeDepth() { return treeDepth; } + public void setTreeDepth(Integer treeDepth) { this.treeDepth = treeDepth; } + public Boolean getVerifySignatures() { return verifySignatures; } + public void setVerifySignatures(Boolean verifySignatures) { this.verifySignatures = verifySignatures; } + public Map getCustomConstraints() { return customConstraints; } + public void setCustomConstraints(Map customConstraints) { this.customConstraints = customConstraints; } + } + + /** Account state in the rollup */ + public static class AccountState { + private String address; + private String balance; + private long nonce; + private String pubkeyHash; + + public String getAddress() { return address; } + public void setAddress(String address) { this.address = address; } + public String getBalance() { return balance; } + public void setBalance(String balance) { this.balance = balance; } + public long getNonce() { return nonce; } + public void setNonce(long nonce) { this.nonce = nonce; } + public String getPubkeyHash() { return pubkeyHash; } + public void setPubkeyHash(String pubkeyHash) { this.pubkeyHash = pubkeyHash; } + } + + /** Transfer operation */ + public static class Transfer { + private String from; + private String to; + private String amount; + private String fee; + private long nonce; + private String signature; + + public Transfer() {} + + public Transfer(String from, String to, String amount, String fee, long nonce) { + this.from = from; + this.to = to; + this.amount = amount; + this.fee = fee; + this.nonce = nonce; + } + + public String getFrom() { return from; } + public void setFrom(String from) { this.from = from; } + public String getTo() { return to; } + public void setTo(String to) { this.to = to; } + public String getAmount() { return amount; } + public void setAmount(String amount) { this.amount = amount; } + public String getFee() { return fee; } + public void setFee(String fee) { this.fee = fee; } + public long getNonce() { return nonce; } + public void setNonce(long nonce) { this.nonce = nonce; } + public String getSignature() { return signature; } + public void setSignature(String signature) { this.signature = signature; } + } + + /** Deposit operation (L1 -> L2) */ + public static class Deposit { + private String l1TxHash; + private String recipient; + private String amount; + private String token; + + public Deposit() {} + + public Deposit(String l1TxHash, String recipient, String amount) { + this.l1TxHash = l1TxHash; + this.recipient = recipient; + this.amount = amount; + } + + public String getL1TxHash() { return l1TxHash; } + public void setL1TxHash(String l1TxHash) { this.l1TxHash = l1TxHash; } + public String getRecipient() { return recipient; } + public void setRecipient(String recipient) { this.recipient = recipient; } + public String getAmount() { return amount; } + public void setAmount(String amount) { this.amount = amount; } + public String getToken() { return token; } + public void setToken(String token) { this.token = token; } + } + + /** Withdrawal operation (L2 -> L1) */ + public static class Withdrawal { + private String sender; + private String recipient; + private String amount; + private String token; + private long nonce; + private String signature; + + public Withdrawal() {} + + public Withdrawal(String sender, String recipient, String amount, long nonce) { + this.sender = sender; + this.recipient = recipient; + this.amount = amount; + this.nonce = nonce; + } + + public String getSender() { return sender; } + public void setSender(String sender) { this.sender = sender; } + public String getRecipient() { return recipient; } + public void setRecipient(String recipient) { this.recipient = recipient; } + public String getAmount() { return amount; } + public void setAmount(String amount) { this.amount = amount; } + public String getToken() { return token; } + public void setToken(String token) { this.token = token; } + public long getNonce() { return nonce; } + public void setNonce(long nonce) { this.nonce = nonce; } + public String getSignature() { return signature; } + public void setSignature(String signature) { this.signature = signature; } + } + + /** Rollup batch */ + public static class Batch { + private long batchNumber; + private String prevStateRoot; + private String newStateRoot; + private List transfers; + private List deposits; + private List withdrawals; + private Proof proof; + private long timestamp; + + public long getBatchNumber() { return batchNumber; } + public void setBatchNumber(long batchNumber) { this.batchNumber = batchNumber; } + public String getPrevStateRoot() { return prevStateRoot; } + public void setPrevStateRoot(String prevStateRoot) { this.prevStateRoot = prevStateRoot; } + public String getNewStateRoot() { return newStateRoot; } + public void setNewStateRoot(String newStateRoot) { this.newStateRoot = newStateRoot; } + public List getTransfers() { return transfers; } + public void setTransfers(List transfers) { this.transfers = transfers; } + public List getDeposits() { return deposits; } + public void setDeposits(List deposits) { this.deposits = deposits; } + public List getWithdrawals() { return withdrawals; } + public void setWithdrawals(List withdrawals) { this.withdrawals = withdrawals; } + public Proof getProof() { return proof; } + public void setProof(Proof proof) { this.proof = proof; } + public long getTimestamp() { return timestamp; } + public void setTimestamp(long timestamp) { this.timestamp = timestamp; } + } + + /** Rollup statistics */ + public static class RollupStats { + private long currentBatch; + private long totalTransactions; + private long totalProofs; + private long avgProofTimeMs; + private String stateRoot; + private long accountCount; + private String tvl; + + public long getCurrentBatch() { return currentBatch; } + public void setCurrentBatch(long currentBatch) { this.currentBatch = currentBatch; } + public long getTotalTransactions() { return totalTransactions; } + public void setTotalTransactions(long totalTransactions) { this.totalTransactions = totalTransactions; } + public long getTotalProofs() { return totalProofs; } + public void setTotalProofs(long totalProofs) { this.totalProofs = totalProofs; } + public long getAvgProofTimeMs() { return avgProofTimeMs; } + public void setAvgProofTimeMs(long avgProofTimeMs) { this.avgProofTimeMs = avgProofTimeMs; } + public String getStateRoot() { return stateRoot; } + public void setStateRoot(String stateRoot) { this.stateRoot = stateRoot; } + public long getAccountCount() { return accountCount; } + public void setAccountCount(long accountCount) { this.accountCount = accountCount; } + public String getTvl() { return tvl; } + public void setTvl(String tvl) { this.tvl = tvl; } + } + + /** Proof generation request */ + public static class ProofRequest { + private CircuitType circuitType; + private List publicInputs; + private List privateInputs; + private ProofSystem system; + + public ProofRequest() {} + + public ProofRequest(CircuitType circuitType, List publicInputs, List privateInputs) { + this.circuitType = circuitType; + this.publicInputs = publicInputs; + this.privateInputs = privateInputs; + } + + public CircuitType getCircuitType() { return circuitType; } + public void setCircuitType(CircuitType circuitType) { this.circuitType = circuitType; } + public List getPublicInputs() { return publicInputs; } + public void setPublicInputs(List publicInputs) { this.publicInputs = publicInputs; } + public List getPrivateInputs() { return privateInputs; } + public void setPrivateInputs(List privateInputs) { this.privateInputs = privateInputs; } + public ProofSystem getSystem() { return system; } + public void setSystem(ProofSystem system) { this.system = system; } + } + + /** Proof verification result */ + public static class VerificationResult { + private boolean valid; + private long verificationTimeMs; + private String error; + + public boolean isValid() { return valid; } + public void setValid(boolean valid) { this.valid = valid; } + public long getVerificationTimeMs() { return verificationTimeMs; } + public void setVerificationTimeMs(long verificationTimeMs) { this.verificationTimeMs = verificationTimeMs; } + public String getError() { return error; } + public void setError(String error) { this.error = error; } + } + + /** Merkle proof for state inclusion */ + public static class MerkleProof { + private String leaf; + private List path; + private List indices; + private String root; + + public String getLeaf() { return leaf; } + public void setLeaf(String leaf) { this.leaf = leaf; } + public List getPath() { return path; } + public void setPath(List path) { this.path = path; } + public List getIndices() { return indices; } + public void setIndices(List indices) { this.indices = indices; } + public String getRoot() { return root; } + public void setRoot(String root) { this.root = root; } + } + + /** Trusted setup ceremony contribution */ + public static class CeremonyContribution { + private String contributorId; + private String hash; + private long timestamp; + private boolean verified; + + public String getContributorId() { return contributorId; } + public void setContributorId(String contributorId) { this.contributorId = contributorId; } + public String getHash() { return hash; } + public void setHash(String hash) { this.hash = hash; } + public long getTimestamp() { return timestamp; } + public void setTimestamp(long timestamp) { this.timestamp = timestamp; } + public boolean isVerified() { return verified; } + public void setVerified(boolean verified) { this.verified = verified; } + } + + /** Trusted setup ceremony */ + public static class TrustedSetup { + private String id; + private CircuitType circuitType; + private ProofSystem system; + private int contributionCount; + private String latestHash; + private boolean complete; + private List contributions; + + public String getId() { return id; } + public void setId(String id) { this.id = id; } + public CircuitType getCircuitType() { return circuitType; } + public void setCircuitType(CircuitType circuitType) { this.circuitType = circuitType; } + public ProofSystem getSystem() { return system; } + public void setSystem(ProofSystem system) { this.system = system; } + public int getContributionCount() { return contributionCount; } + public void setContributionCount(int contributionCount) { this.contributionCount = contributionCount; } + public String getLatestHash() { return latestHash; } + public void setLatestHash(String latestHash) { this.latestHash = latestHash; } + public boolean isComplete() { return complete; } + public void setComplete(boolean complete) { this.complete = complete; } + public List getContributions() { return contributions; } + public void setContributions(List contributions) { this.contributions = contributions; } + } + + /** ZK SDK configuration */ + public static class ZkConfig { + private String apiKey; + private String endpoint = "https://zk.synor.io/v1"; + private String wsEndpoint = "wss://zk.synor.io/v1/ws"; + private int timeout = 60000; + private int retries = 3; + private ProofSystem defaultProofSystem = ProofSystem.GROTH16; + private boolean debug = false; + + public ZkConfig(String apiKey) { + this.apiKey = apiKey; + } + + public String getApiKey() { return apiKey; } + public void setApiKey(String apiKey) { this.apiKey = apiKey; } + public String getEndpoint() { return endpoint; } + public void setEndpoint(String endpoint) { this.endpoint = endpoint; } + public String getWsEndpoint() { return wsEndpoint; } + public void setWsEndpoint(String wsEndpoint) { this.wsEndpoint = wsEndpoint; } + public int getTimeout() { return timeout; } + public void setTimeout(int timeout) { this.timeout = timeout; } + public int getRetries() { return retries; } + public void setRetries(int retries) { this.retries = retries; } + public ProofSystem getDefaultProofSystem() { return defaultProofSystem; } + public void setDefaultProofSystem(ProofSystem defaultProofSystem) { this.defaultProofSystem = defaultProofSystem; } + public boolean isDebug() { return debug; } + public void setDebug(boolean debug) { this.debug = debug; } + } + + /** ZK SDK exception */ + public static class ZkException extends RuntimeException { + private final String code; + private final Integer status; + + public ZkException(String message) { + super(message); + this.code = null; + this.status = null; + } + + public ZkException(String message, String code) { + super(message); + this.code = code; + this.status = null; + } + + public ZkException(String message, String code, Integer status) { + super(message); + this.code = code; + this.status = status; + } + + public String getCode() { return code; } + public Integer getStatus() { return status; } + } + + /** Proof system characteristics */ + public static class ProofSystemInfo { + public final String name; + public final int proofSize; + public final int verificationTimeMs; + public final boolean trustedSetup; + public final boolean universal; + + public ProofSystemInfo(String name, int proofSize, int verificationTimeMs, + boolean trustedSetup, boolean universal) { + this.name = name; + this.proofSize = proofSize; + this.verificationTimeMs = verificationTimeMs; + this.trustedSetup = trustedSetup; + this.universal = universal; + } + } + + public static ProofSystemInfo getProofSystemInfo(ProofSystem system) { + switch (system) { + case GROTH16: + return new ProofSystemInfo("Groth16", 192, 10, true, false); + case PLONK: + return new ProofSystemInfo("PLONK", 512, 15, true, true); + case STARK: + return new ProofSystemInfo("STARK", 50000, 30, false, true); + default: + return null; + } + } + + // Constants + public static final int MAX_BATCH_SIZE = 1000; + public static final int STATE_TREE_DEPTH = 32; + public static final int PROOF_EXPIRY_BLOCKS = 100; +} diff --git a/sdk/js/src/zk/index.ts b/sdk/js/src/zk/index.ts new file mode 100644 index 0000000..47a4131 --- /dev/null +++ b/sdk/js/src/zk/index.ts @@ -0,0 +1,455 @@ +// Synor ZK SDK for JavaScript/TypeScript +// +// Zero-Knowledge proof systems for ZK-Rollups and privacy. + +import { + ZkConfig, + ZkError, + DEFAULT_CONFIG, + ProofSystem, + CircuitType, + Proof, + VerificationKey, + ProvingKey, + CircuitConfig, + ProofRequest, + VerificationResult, + Batch, + RollupStats, + Transfer, + Deposit, + Withdrawal, + AccountState, + MerkleProof, + TrustedSetup, + CeremonyContribution, +} from './types'; + +export * from './types'; + +/** + * Synor ZK SDK Client + * + * Provides zero-knowledge proof generation and verification for ZK-Rollups. + * + * @example + * ```typescript + * const zk = new SynorZk({ apiKey: 'your-api-key' }); + * + * // Generate a proof + * const proof = await zk.proofs.generate({ + * circuitType: CircuitType.Transfer, + * publicInputs: [...], + * privateInputs: [...], + * }); + * + * // Verify the proof + * const result = await zk.proofs.verify(proof); + * console.log('Valid:', result.valid); + * ``` + */ +export class SynorZk { + private readonly config: Required; + private closed = false; + + public readonly proofs: ProofsClient; + public readonly circuits: CircuitsClient; + public readonly rollup: RollupClient; + public readonly state: StateClient; + public readonly ceremony: CeremonyClient; + + constructor(config: ZkConfig) { + this.config = { + ...DEFAULT_CONFIG, + ...config, + } as Required; + + this.proofs = new ProofsClient(this); + this.circuits = new CircuitsClient(this); + this.rollup = new RollupClient(this); + this.state = new StateClient(this); + this.ceremony = new CeremonyClient(this); + } + + /** + * Get the default proof system + */ + get defaultProofSystem(): ProofSystem { + return this.config.defaultProofSystem; + } + + /** + * Health check + */ + async healthCheck(): Promise { + try { + const result = await this.get<{ status: string }>('/health'); + return result.status === 'healthy'; + } catch { + return false; + } + } + + /** + * Get service info + */ + async getInfo(): Promise> { + return this.get('/info'); + } + + /** + * Close the client + */ + close(): void { + this.closed = true; + } + + /** + * Check if client is closed + */ + get isClosed(): boolean { + return this.closed; + } + + // Internal HTTP methods + async get(path: string): Promise { + return this.request('GET', path); + } + + async post(path: string, body?: unknown): Promise { + return this.request('POST', path, body); + } + + async delete(path: string): Promise { + return this.request('DELETE', path); + } + + private async request(method: string, path: string, body?: unknown): Promise { + if (this.closed) { + throw new ZkError('Client has been closed', 'CLIENT_CLOSED'); + } + + const url = `${this.config.endpoint}${path}`; + const headers: Record = { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${this.config.apiKey}`, + 'X-SDK-Version': 'js/0.1.0', + }; + + let lastError: Error | null = null; + + for (let attempt = 0; attempt <= this.config.retries; attempt++) { + try { + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), this.config.timeout); + + const response = await fetch(url, { + method, + headers, + body: body ? JSON.stringify(body) : undefined, + signal: controller.signal, + }); + + clearTimeout(timeoutId); + + if (!response.ok) { + const error = await response.json().catch(() => ({})); + throw new ZkError( + error.message || `HTTP ${response.status}`, + error.code, + response.status + ); + } + + return await response.json(); + } catch (error) { + lastError = error as Error; + if (error instanceof ZkError) throw error; + if (attempt < this.config.retries) { + await new Promise((r) => setTimeout(r, 100 * Math.pow(2, attempt))); + } + } + } + + throw new ZkError(lastError?.message || 'Request failed', 'NETWORK_ERROR'); + } +} + +/** + * Proofs sub-client + */ +export class ProofsClient { + constructor(private readonly zk: SynorZk) {} + + /** + * Generate a zero-knowledge proof + */ + async generate(request: ProofRequest): Promise { + return this.zk.post('/proofs/generate', { + circuit_type: request.circuitType, + public_inputs: request.publicInputs, + private_inputs: request.privateInputs, + system: request.system || this.zk.defaultProofSystem, + }); + } + + /** + * Verify a proof + */ + async verify(proof: Proof): Promise { + return this.zk.post('/proofs/verify', { + proof_id: proof.id, + data: proof.data, + public_inputs: proof.publicInputs, + system: proof.system, + circuit_type: proof.circuitType, + }); + } + + /** + * Get a proof by ID + */ + async get(proofId: string): Promise { + return this.zk.get(`/proofs/${proofId}`); + } + + /** + * List recent proofs + */ + async list(options?: { limit?: number; offset?: number }): Promise { + const params = new URLSearchParams(); + if (options?.limit) params.set('limit', options.limit.toString()); + if (options?.offset) params.set('offset', options.offset.toString()); + const query = params.toString(); + const result = await this.zk.get<{ proofs: Proof[] }>(`/proofs${query ? `?${query}` : ''}`); + return result.proofs || []; + } + + /** + * Serialize a proof to bytes + */ + async serialize(proof: Proof): Promise { + const result = await this.zk.post<{ data: string }>('/proofs/serialize', { + proof_id: proof.id, + }); + return Uint8Array.from(atob(result.data), (c) => c.charCodeAt(0)); + } + + /** + * Deserialize bytes to a proof + */ + async deserialize(data: Uint8Array, system: ProofSystem): Promise { + const base64 = btoa(String.fromCharCode(...data)); + return this.zk.post('/proofs/deserialize', { + data: base64, + system, + }); + } +} + +/** + * Circuits sub-client + */ +export class CircuitsClient { + constructor(private readonly zk: SynorZk) {} + + /** + * Get verification key for a circuit + */ + async getVerificationKey(circuitType: CircuitType, system?: ProofSystem): Promise { + return this.zk.get(`/circuits/${circuitType}/vk?system=${system || this.zk.defaultProofSystem}`); + } + + /** + * Get proving key for a circuit + */ + async getProvingKey(circuitType: CircuitType, system?: ProofSystem): Promise { + return this.zk.get(`/circuits/${circuitType}/pk?system=${system || this.zk.defaultProofSystem}`); + } + + /** + * List available circuits + */ + async list(): Promise { + const result = await this.zk.get<{ circuits: CircuitConfig[] }>('/circuits'); + return result.circuits || []; + } + + /** + * Get circuit configuration + */ + async getConfig(circuitType: CircuitType): Promise { + return this.zk.get(`/circuits/${circuitType}/config`); + } + + /** + * Compile a custom circuit + */ + async compile(config: CircuitConfig): Promise<{ circuitId: string }> { + return this.zk.post('/circuits/compile', config); + } +} + +/** + * Rollup sub-client + */ +export class RollupClient { + constructor(private readonly zk: SynorZk) {} + + /** + * Get rollup statistics + */ + async getStats(): Promise { + return this.zk.get('/rollup/stats'); + } + + /** + * Get current batch + */ + async getCurrentBatch(): Promise { + return this.zk.get('/rollup/batch/current'); + } + + /** + * Get batch by number + */ + async getBatch(batchNumber: number): Promise { + return this.zk.get(`/rollup/batch/${batchNumber}`); + } + + /** + * Submit a transfer + */ + async submitTransfer(transfer: Transfer): Promise<{ txId: string }> { + return this.zk.post('/rollup/transfer', transfer); + } + + /** + * Submit a deposit + */ + async submitDeposit(deposit: Deposit): Promise<{ txId: string }> { + return this.zk.post('/rollup/deposit', deposit); + } + + /** + * Submit a withdrawal + */ + async submitWithdrawal(withdrawal: Withdrawal): Promise<{ txId: string }> { + return this.zk.post('/rollup/withdraw', withdrawal); + } + + /** + * Finalize current batch (generates proof and submits to L1) + */ + async finalizeBatch(): Promise { + return this.zk.post('/rollup/batch/finalize', {}); + } + + /** + * Get pending transactions + */ + async getPendingTransactions(): Promise { + const result = await this.zk.get<{ transactions: Transfer[] }>('/rollup/pending'); + return result.transactions || []; + } +} + +/** + * State sub-client + */ +export class StateClient { + constructor(private readonly zk: SynorZk) {} + + /** + * Get current state root + */ + async getRoot(): Promise { + const result = await this.zk.get<{ root: string }>('/state/root'); + return result.root; + } + + /** + * Get account state + */ + async getAccount(address: string): Promise { + return this.zk.get(`/state/account/${address}`); + } + + /** + * Get merkle proof for account inclusion + */ + async getMerkleProof(address: string): Promise { + return this.zk.get(`/state/proof/${address}`); + } + + /** + * Verify merkle proof + */ + async verifyMerkleProof(proof: MerkleProof): Promise { + const result = await this.zk.post<{ valid: boolean }>('/state/proof/verify', proof); + return result.valid; + } + + /** + * Get state at specific batch + */ + async getStateAtBatch(batchNumber: number): Promise<{ root: string; accountCount: number }> { + return this.zk.get(`/state/batch/${batchNumber}`); + } +} + +/** + * Ceremony sub-client (trusted setup) + */ +export class CeremonyClient { + constructor(private readonly zk: SynorZk) {} + + /** + * Get ceremony status + */ + async getStatus(circuitType: CircuitType, system?: ProofSystem): Promise { + return this.zk.get(`/ceremony/${circuitType}?system=${system || this.zk.defaultProofSystem}`); + } + + /** + * Contribute to ceremony + */ + async contribute( + circuitType: CircuitType, + entropy: Uint8Array, + system?: ProofSystem + ): Promise { + const entropyBase64 = btoa(String.fromCharCode(...entropy)); + return this.zk.post('/ceremony/contribute', { + circuit_type: circuitType, + entropy: entropyBase64, + system: system || this.zk.defaultProofSystem, + }); + } + + /** + * Verify a contribution + */ + async verifyContribution( + circuitType: CircuitType, + contributionHash: string + ): Promise<{ valid: boolean }> { + return this.zk.post('/ceremony/verify', { + circuit_type: circuitType, + contribution_hash: contributionHash, + }); + } + + /** + * List contributions + */ + async listContributions(circuitType: CircuitType): Promise { + const result = await this.zk.get<{ contributions: CeremonyContribution[] }>( + `/ceremony/${circuitType}/contributions` + ); + return result.contributions || []; + } +} + +// Default export +export default SynorZk; diff --git a/sdk/js/src/zk/types.ts b/sdk/js/src/zk/types.ts new file mode 100644 index 0000000..94e4f32 --- /dev/null +++ b/sdk/js/src/zk/types.ts @@ -0,0 +1,401 @@ +// Synor ZK SDK Types for JavaScript/TypeScript +// +// Zero-Knowledge proof systems for ZK-Rollups and privacy. + +/** + * Proof system backends + */ +export enum ProofSystem { + /** Groth16 - smallest proofs (~192 bytes), fastest verification */ + Groth16 = 'groth16', + /** PLONK - universal trusted setup, medium proofs (~512 bytes) */ + Plonk = 'plonk', + /** STARK - no trusted setup, largest proofs (~50KB) */ + Stark = 'stark', +} + +/** + * Circuit types for different operations + */ +export enum CircuitType { + /** Single transfer between accounts */ + Transfer = 'transfer', + /** Batch of multiple transfers */ + Batch = 'batch', + /** Deposit from L1 to L2 */ + Deposit = 'deposit', + /** Withdrawal from L2 to L1 */ + Withdraw = 'withdraw', +} + +/** + * Rollup state + */ +export enum RollupState { + /** Rollup is accepting transactions */ + Active = 'active', + /** Rollup is paused */ + Paused = 'paused', + /** Rollup is finalizing */ + Finalizing = 'finalizing', + /** Rollup has been finalized */ + Finalized = 'finalized', +} + +/** + * Proof status + */ +export enum ProofStatus { + /** Proof is being generated */ + Generating = 'generating', + /** Proof generation completed */ + Completed = 'completed', + /** Proof generation failed */ + Failed = 'failed', + /** Proof has been verified */ + Verified = 'verified', +} + +/** + * Zero-knowledge proof + */ +export interface Proof { + /** Unique proof identifier */ + id: string; + /** Proof system used */ + system: ProofSystem; + /** Circuit type */ + circuitType: CircuitType; + /** Serialized proof data (base64) */ + data: string; + /** Public inputs (base64 encoded) */ + publicInputs: string[]; + /** Proof size in bytes */ + size: number; + /** Generation time in milliseconds */ + generationTimeMs: number; + /** Timestamp when proof was created */ + createdAt: number; +} + +/** + * Verification key for a circuit + */ +export interface VerificationKey { + /** Circuit identifier */ + circuitId: string; + /** Circuit type */ + circuitType: CircuitType; + /** Proof system */ + system: ProofSystem; + /** Serialized verification key (base64) */ + data: string; + /** Key size in bytes */ + size: number; +} + +/** + * Proving key for generating proofs + */ +export interface ProvingKey { + /** Circuit identifier */ + circuitId: string; + /** Circuit type */ + circuitType: CircuitType; + /** Proof system */ + system: ProofSystem; + /** Serialized proving key (base64) */ + data: string; + /** Key size in bytes */ + size: number; +} + +/** + * Circuit configuration + */ +export interface CircuitConfig { + /** Circuit type */ + type: CircuitType; + /** Maximum batch size for batch circuits */ + maxBatchSize?: number; + /** State tree depth */ + treeDepth?: number; + /** Whether to verify signatures in circuit */ + verifySignatures?: boolean; + /** Custom constraints */ + customConstraints?: Record; +} + +/** + * State tree node + */ +export interface StateNode { + /** Node hash */ + hash: string; + /** Left child hash (if internal node) */ + left?: string; + /** Right child hash (if internal node) */ + right?: string; + /** Account data (if leaf node) */ + account?: AccountState; +} + +/** + * Account state in the rollup + */ +export interface AccountState { + /** Account address */ + address: string; + /** Account balance */ + balance: string; + /** Account nonce */ + nonce: number; + /** Public key hash */ + pubkeyHash: string; +} + +/** + * Transfer operation + */ +export interface Transfer { + /** Sender address */ + from: string; + /** Recipient address */ + to: string; + /** Amount to transfer */ + amount: string; + /** Fee */ + fee: string; + /** Nonce */ + nonce: number; + /** Signature */ + signature?: string; +} + +/** + * Deposit operation (L1 -> L2) + */ +export interface Deposit { + /** L1 transaction hash */ + l1TxHash: string; + /** Recipient address on L2 */ + recipient: string; + /** Amount deposited */ + amount: string; + /** Token address (or native if null) */ + token?: string; +} + +/** + * Withdrawal operation (L2 -> L1) + */ +export interface Withdrawal { + /** Sender address on L2 */ + sender: string; + /** Recipient address on L1 */ + recipient: string; + /** Amount to withdraw */ + amount: string; + /** Token address (or native if null) */ + token?: string; + /** Nonce */ + nonce: number; + /** Signature */ + signature?: string; +} + +/** + * Rollup batch + */ +export interface Batch { + /** Batch number */ + batchNumber: number; + /** Previous state root */ + prevStateRoot: string; + /** New state root after batch */ + newStateRoot: string; + /** Transfers in this batch */ + transfers: Transfer[]; + /** Deposits in this batch */ + deposits: Deposit[]; + /** Withdrawals in this batch */ + withdrawals: Withdrawal[]; + /** Batch proof (once generated) */ + proof?: Proof; + /** Timestamp */ + timestamp: number; +} + +/** + * Rollup statistics + */ +export interface RollupStats { + /** Current batch number */ + currentBatch: number; + /** Total transactions processed */ + totalTransactions: number; + /** Total proofs generated */ + totalProofs: number; + /** Average proof generation time (ms) */ + avgProofTimeMs: number; + /** Current state root */ + stateRoot: string; + /** Number of accounts */ + accountCount: number; + /** Total value locked */ + tvl: string; +} + +/** + * Proof generation request + */ +export interface ProofRequest { + /** Circuit type */ + circuitType: CircuitType; + /** Public inputs */ + publicInputs: string[]; + /** Private inputs (witness) */ + privateInputs: string[]; + /** Proof system to use */ + system?: ProofSystem; +} + +/** + * Proof verification result + */ +export interface VerificationResult { + /** Whether the proof is valid */ + valid: boolean; + /** Verification time in milliseconds */ + verificationTimeMs: number; + /** Error message if invalid */ + error?: string; +} + +/** + * Setup ceremony contribution + */ +export interface CeremonyContribution { + /** Contributor identifier */ + contributorId: string; + /** Contribution hash */ + hash: string; + /** Timestamp */ + timestamp: number; + /** Verification status */ + verified: boolean; +} + +/** + * Trusted setup ceremony + */ +export interface TrustedSetup { + /** Ceremony identifier */ + id: string; + /** Circuit type */ + circuitType: CircuitType; + /** Proof system */ + system: ProofSystem; + /** Number of contributions */ + contributionCount: number; + /** Latest contribution hash */ + latestHash: string; + /** Whether ceremony is complete */ + complete: boolean; + /** Contributions */ + contributions: CeremonyContribution[]; +} + +/** + * Merkle proof for state inclusion + */ +export interface MerkleProof { + /** Leaf value */ + leaf: string; + /** Path from leaf to root (sibling hashes) */ + path: string[]; + /** Path indices (0 = left, 1 = right) */ + indices: number[]; + /** Root hash */ + root: string; +} + +/** + * ZK SDK configuration + */ +export interface ZkConfig { + /** API key for authentication */ + apiKey: string; + /** API endpoint */ + endpoint?: string; + /** WebSocket endpoint for subscriptions */ + wsEndpoint?: string; + /** Request timeout in milliseconds */ + timeout?: number; + /** Number of retries for failed requests */ + retries?: number; + /** Default proof system */ + defaultProofSystem?: ProofSystem; + /** Enable debug logging */ + debug?: boolean; +} + +/** + * ZK SDK error + */ +export class ZkError extends Error { + constructor( + message: string, + public readonly code?: string, + public readonly status?: number + ) { + super(message); + this.name = 'ZkError'; + } +} + +/** + * Default configuration values + */ +export const DEFAULT_CONFIG = { + endpoint: 'https://zk.synor.io/v1', + wsEndpoint: 'wss://zk.synor.io/v1/ws', + timeout: 60000, // ZK proofs can take longer + retries: 3, + defaultProofSystem: ProofSystem.Groth16, + debug: false, +} as const; + +/** + * Proof system characteristics + */ +export const PROOF_SYSTEM_INFO = { + [ProofSystem.Groth16]: { + name: 'Groth16', + proofSize: 192, + verificationTimeMs: 10, + trustedSetup: true, + universal: false, + }, + [ProofSystem.Plonk]: { + name: 'PLONK', + proofSize: 512, + verificationTimeMs: 15, + trustedSetup: true, + universal: true, + }, + [ProofSystem.Stark]: { + name: 'STARK', + proofSize: 50000, + verificationTimeMs: 30, + trustedSetup: false, + universal: true, + }, +} as const; + +/** + * Constants + */ +export const MAX_BATCH_SIZE = 1000; +export const STATE_TREE_DEPTH = 32; +export const PROOF_EXPIRY_BLOCKS = 100; diff --git a/sdk/kotlin/src/main/kotlin/io/synor/zk/SynorZk.kt b/sdk/kotlin/src/main/kotlin/io/synor/zk/SynorZk.kt new file mode 100644 index 0000000..ac54f84 --- /dev/null +++ b/sdk/kotlin/src/main/kotlin/io/synor/zk/SynorZk.kt @@ -0,0 +1,449 @@ +package io.synor.zk + +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import kotlinx.serialization.* +import kotlinx.serialization.json.* +import java.net.URI +import java.net.http.HttpClient +import java.net.http.HttpRequest +import java.net.http.HttpResponse +import java.time.Duration +import java.util.* +import java.util.concurrent.atomic.AtomicBoolean + +/** + * Synor ZK SDK for Kotlin + * + * Zero-Knowledge proof systems for ZK-Rollups and privacy. + */ + +// Enums +enum class ProofSystem(val value: String) { + @SerialName("groth16") GROTH16("groth16"), + @SerialName("plonk") PLONK("plonk"), + @SerialName("stark") STARK("stark"); + + companion object { + fun fromString(value: String) = entries.find { it.value == value } ?: GROTH16 + } +} + +enum class CircuitType(val value: String) { + @SerialName("transfer") TRANSFER("transfer"), + @SerialName("batch") BATCH("batch"), + @SerialName("deposit") DEPOSIT("deposit"), + @SerialName("withdraw") WITHDRAW("withdraw"); + + companion object { + fun fromString(value: String) = entries.find { it.value == value } ?: TRANSFER + } +} + +enum class RollupState { ACTIVE, PAUSED, FINALIZING, FINALIZED } +enum class ProofStatus { GENERATING, COMPLETED, FAILED, VERIFIED } + +// Data classes +@Serializable +data class Proof( + val id: String, + val system: ProofSystem, + @SerialName("circuit_type") val circuitType: CircuitType, + val data: String, + @SerialName("public_inputs") val publicInputs: List, + val size: Int, + @SerialName("generation_time_ms") val generationTimeMs: Long, + @SerialName("created_at") val createdAt: Long +) + +@Serializable +data class VerificationKey( + @SerialName("circuit_id") val circuitId: String, + @SerialName("circuit_type") val circuitType: CircuitType, + val system: ProofSystem, + val data: String, + val size: Int +) + +@Serializable +data class ProvingKey( + @SerialName("circuit_id") val circuitId: String, + @SerialName("circuit_type") val circuitType: CircuitType, + val system: ProofSystem, + val data: String, + val size: Int +) + +@Serializable +data class CircuitConfig( + val type: CircuitType, + @SerialName("max_batch_size") val maxBatchSize: Int? = null, + @SerialName("tree_depth") val treeDepth: Int? = null, + @SerialName("verify_signatures") val verifySignatures: Boolean? = null, + @SerialName("custom_constraints") val customConstraints: JsonObject? = null +) + +@Serializable +data class AccountState( + val address: String, + val balance: String, + val nonce: Long, + @SerialName("pubkey_hash") val pubkeyHash: String +) + +@Serializable +data class Transfer( + val from: String, + val to: String, + val amount: String, + val fee: String, + val nonce: Long, + val signature: String? = null +) + +@Serializable +data class Deposit( + @SerialName("l1_tx_hash") val l1TxHash: String, + val recipient: String, + val amount: String, + val token: String? = null +) + +@Serializable +data class Withdrawal( + val sender: String, + val recipient: String, + val amount: String, + val token: String? = null, + val nonce: Long, + val signature: String? = null +) + +@Serializable +data class Batch( + @SerialName("batch_number") val batchNumber: Long, + @SerialName("prev_state_root") val prevStateRoot: String, + @SerialName("new_state_root") val newStateRoot: String, + val transfers: List, + val deposits: List, + val withdrawals: List, + val proof: Proof? = null, + val timestamp: Long +) + +@Serializable +data class RollupStats( + @SerialName("current_batch") val currentBatch: Long, + @SerialName("total_transactions") val totalTransactions: Long, + @SerialName("total_proofs") val totalProofs: Long, + @SerialName("avg_proof_time_ms") val avgProofTimeMs: Long, + @SerialName("state_root") val stateRoot: String, + @SerialName("account_count") val accountCount: Long, + val tvl: String +) + +@Serializable +data class ProofRequest( + @SerialName("circuit_type") val circuitType: CircuitType, + @SerialName("public_inputs") val publicInputs: List, + @SerialName("private_inputs") val privateInputs: List, + val system: ProofSystem? = null +) + +@Serializable +data class VerificationResult( + val valid: Boolean, + @SerialName("verification_time_ms") val verificationTimeMs: Long, + val error: String? = null +) + +@Serializable +data class MerkleProof( + val leaf: String, + val path: List, + val indices: List, + val root: String +) + +@Serializable +data class CeremonyContribution( + @SerialName("contributor_id") val contributorId: String, + val hash: String, + val timestamp: Long, + val verified: Boolean +) + +@Serializable +data class TrustedSetup( + val id: String, + @SerialName("circuit_type") val circuitType: CircuitType, + val system: ProofSystem, + @SerialName("contribution_count") val contributionCount: Int, + @SerialName("latest_hash") val latestHash: String, + val complete: Boolean, + val contributions: List +) + +data class ZkConfig( + val apiKey: String, + val endpoint: String = "https://zk.synor.io/v1", + val wsEndpoint: String = "wss://zk.synor.io/v1/ws", + val timeout: Long = 60000, + val retries: Int = 3, + val defaultProofSystem: ProofSystem = ProofSystem.GROTH16, + val debug: Boolean = false +) + +class ZkException( + message: String, + val code: String? = null, + val status: Int? = null +) : RuntimeException(message) + +data class ProofSystemInfo( + val name: String, + val proofSize: Int, + val verificationTimeMs: Int, + val trustedSetup: Boolean, + val universal: Boolean +) + +fun getProofSystemInfo(system: ProofSystem): ProofSystemInfo = when (system) { + ProofSystem.GROTH16 -> ProofSystemInfo("Groth16", 192, 10, true, false) + ProofSystem.PLONK -> ProofSystemInfo("PLONK", 512, 15, true, true) + ProofSystem.STARK -> ProofSystemInfo("STARK", 50000, 30, false, true) +} + +const val MAX_BATCH_SIZE = 1000 +const val STATE_TREE_DEPTH = 32 +const val PROOF_EXPIRY_BLOCKS = 100 + +/** + * Main ZK SDK client + */ +class SynorZk(private val config: ZkConfig) { + private val httpClient = HttpClient.newBuilder() + .connectTimeout(Duration.ofMillis(config.timeout)) + .build() + private val json = Json { ignoreUnknownKeys = true } + private val closed = AtomicBoolean(false) + + val proofs = ProofsClient(this) + val circuits = CircuitsClient(this) + val rollup = RollupClient(this) + val state = StateClient(this) + val ceremony = CeremonyClient(this) + + val defaultProofSystem: ProofSystem get() = config.defaultProofSystem + + suspend fun healthCheck(): Boolean = try { + val result = get("/health") + result["status"]?.jsonPrimitive?.content == "healthy" + } catch (e: Exception) { + false + } + + suspend fun getInfo(): JsonObject = get("/info") + + fun close() { closed.set(true) } + val isClosed: Boolean get() = closed.get() + + internal suspend inline fun get(path: String): T = request("GET", path, null) + internal suspend inline fun post(path: String, body: Any?): T = request("POST", path, body) + + internal suspend inline fun request(method: String, path: String, body: Any?): T { + if (closed.get()) throw ZkException("Client has been closed", "CLIENT_CLOSED") + + return withContext(Dispatchers.IO) { + val url = "${config.endpoint}$path" + val requestBuilder = HttpRequest.newBuilder() + .uri(URI.create(url)) + .header("Content-Type", "application/json") + .header("Authorization", "Bearer ${config.apiKey}") + .header("X-SDK-Version", "kotlin/0.1.0") + .timeout(Duration.ofMillis(config.timeout)) + + when (method) { + "GET" -> requestBuilder.GET() + "POST" -> { + val jsonBody = if (body != null) json.encodeToString(body as? JsonElement ?: buildJsonObject { + (body as? Map<*, *>)?.forEach { (k, v) -> + put(k.toString(), when (v) { + is String -> JsonPrimitive(v) + is Number -> JsonPrimitive(v) + is Boolean -> JsonPrimitive(v) + is List<*> -> JsonArray(v.map { JsonPrimitive(it.toString()) }) + else -> JsonPrimitive(v.toString()) + }) + } + }) else "{}" + requestBuilder.POST(HttpRequest.BodyPublishers.ofString(jsonBody)) + } + "DELETE" -> requestBuilder.DELETE() + } + + val response = httpClient.send(requestBuilder.build(), HttpResponse.BodyHandlers.ofString()) + + if (response.statusCode() >= 400) { + val error = json.parseToJsonElement(response.body()).jsonObject + throw ZkException( + error["message"]?.jsonPrimitive?.content ?: "HTTP ${response.statusCode()}", + error["code"]?.jsonPrimitive?.content, + response.statusCode() + ) + } + + json.decodeFromString(response.body()) + } + } +} + +class ProofsClient(private val zk: SynorZk) { + suspend fun generate(request: ProofRequest): Proof { + val body = buildJsonObject { + put("circuit_type", request.circuitType.value) + put("public_inputs", JsonArray(request.publicInputs.map { JsonPrimitive(it) })) + put("private_inputs", JsonArray(request.privateInputs.map { JsonPrimitive(it) })) + put("system", (request.system ?: zk.defaultProofSystem).value) + } + return zk.post("/proofs/generate", body) + } + + suspend fun verify(proof: Proof): VerificationResult { + val body = buildJsonObject { + put("proof_id", proof.id) + put("data", proof.data) + put("public_inputs", JsonArray(proof.publicInputs.map { JsonPrimitive(it) })) + put("system", proof.system.value) + put("circuit_type", proof.circuitType.value) + } + return zk.post("/proofs/verify", body) + } + + suspend fun get(proofId: String): Proof = zk.get("/proofs/$proofId") + + suspend fun list(limit: Int? = null, offset: Int? = null): List { + val params = listOfNotNull( + limit?.let { "limit=$it" }, + offset?.let { "offset=$it" } + ).joinToString("&") + val path = if (params.isNotEmpty()) "/proofs?$params" else "/proofs" + val result: JsonObject = zk.get(path) + return result["proofs"]?.jsonArray?.map { Json.decodeFromJsonElement(it) } ?: emptyList() + } + + suspend fun serialize(proof: Proof): ByteArray { + val body = buildJsonObject { put("proof_id", proof.id) } + val result: JsonObject = zk.post("/proofs/serialize", body) + return Base64.getDecoder().decode(result["data"]?.jsonPrimitive?.content) + } + + suspend fun deserialize(data: ByteArray, system: ProofSystem): Proof { + val body = buildJsonObject { + put("data", Base64.getEncoder().encodeToString(data)) + put("system", system.value) + } + return zk.post("/proofs/deserialize", body) + } +} + +class CircuitsClient(private val zk: SynorZk) { + suspend fun getVerificationKey(circuitType: CircuitType, system: ProofSystem? = null): VerificationKey { + val sys = system ?: zk.defaultProofSystem + return zk.get("/circuits/${circuitType.value}/vk?system=${sys.value}") + } + + suspend fun getProvingKey(circuitType: CircuitType, system: ProofSystem? = null): ProvingKey { + val sys = system ?: zk.defaultProofSystem + return zk.get("/circuits/${circuitType.value}/pk?system=${sys.value}") + } + + suspend fun list(): List { + val result: JsonObject = zk.get("/circuits") + return result["circuits"]?.jsonArray?.map { Json.decodeFromJsonElement(it) } ?: emptyList() + } + + suspend fun getConfig(circuitType: CircuitType): CircuitConfig = + zk.get("/circuits/${circuitType.value}/config") + + suspend fun compile(config: CircuitConfig): String { + val result: JsonObject = zk.post("/circuits/compile", Json.encodeToJsonElement(config)) + return result["circuit_id"]?.jsonPrimitive?.content ?: "" + } +} + +class RollupClient(private val zk: SynorZk) { + suspend fun getStats(): RollupStats = zk.get("/rollup/stats") + suspend fun getCurrentBatch(): Batch = zk.get("/rollup/batch/current") + suspend fun getBatch(batchNumber: Long): Batch = zk.get("/rollup/batch/$batchNumber") + + suspend fun submitTransfer(transfer: Transfer): String { + val result: JsonObject = zk.post("/rollup/transfer", Json.encodeToJsonElement(transfer)) + return result["tx_id"]?.jsonPrimitive?.content ?: "" + } + + suspend fun submitDeposit(deposit: Deposit): String { + val result: JsonObject = zk.post("/rollup/deposit", Json.encodeToJsonElement(deposit)) + return result["tx_id"]?.jsonPrimitive?.content ?: "" + } + + suspend fun submitWithdrawal(withdrawal: Withdrawal): String { + val result: JsonObject = zk.post("/rollup/withdraw", Json.encodeToJsonElement(withdrawal)) + return result["tx_id"]?.jsonPrimitive?.content ?: "" + } + + suspend fun finalizeBatch(): Batch = zk.post("/rollup/batch/finalize", buildJsonObject {}) + + suspend fun getPendingTransactions(): List { + val result: JsonObject = zk.get("/rollup/pending") + return result["transactions"]?.jsonArray?.map { Json.decodeFromJsonElement(it) } ?: emptyList() + } +} + +class StateClient(private val zk: SynorZk) { + suspend fun getRoot(): String { + val result: JsonObject = zk.get("/state/root") + return result["root"]?.jsonPrimitive?.content ?: "" + } + + suspend fun getAccount(address: String): AccountState = zk.get("/state/account/$address") + suspend fun getMerkleProof(address: String): MerkleProof = zk.get("/state/proof/$address") + + suspend fun verifyMerkleProof(proof: MerkleProof): Boolean { + val result: JsonObject = zk.post("/state/proof/verify", Json.encodeToJsonElement(proof)) + return result["valid"]?.jsonPrimitive?.boolean ?: false + } + + suspend fun getStateAtBatch(batchNumber: Long): JsonObject = zk.get("/state/batch/$batchNumber") +} + +class CeremonyClient(private val zk: SynorZk) { + suspend fun getStatus(circuitType: CircuitType, system: ProofSystem? = null): TrustedSetup { + val sys = system ?: zk.defaultProofSystem + return zk.get("/ceremony/${circuitType.value}?system=${sys.value}") + } + + suspend fun contribute(circuitType: CircuitType, entropy: ByteArray, system: ProofSystem? = null): CeremonyContribution { + val sys = system ?: zk.defaultProofSystem + val body = buildJsonObject { + put("circuit_type", circuitType.value) + put("entropy", Base64.getEncoder().encodeToString(entropy)) + put("system", sys.value) + } + return zk.post("/ceremony/contribute", body) + } + + suspend fun verifyContribution(circuitType: CircuitType, contributionHash: String): Boolean { + val body = buildJsonObject { + put("circuit_type", circuitType.value) + put("contribution_hash", contributionHash) + } + val result: JsonObject = zk.post("/ceremony/verify", body) + return result["valid"]?.jsonPrimitive?.boolean ?: false + } + + suspend fun listContributions(circuitType: CircuitType): List { + val result: JsonObject = zk.get("/ceremony/${circuitType.value}/contributions") + return result["contributions"]?.jsonArray?.map { Json.decodeFromJsonElement(it) } ?: emptyList() + } +} diff --git a/sdk/python/src/synor_zk/__init__.py b/sdk/python/src/synor_zk/__init__.py new file mode 100644 index 0000000..ddf0101 --- /dev/null +++ b/sdk/python/src/synor_zk/__init__.py @@ -0,0 +1,62 @@ +""" +Synor ZK SDK for Python + +Zero-Knowledge proof systems for ZK-Rollups and privacy. +""" + +from .types import ( + ProofSystem, + CircuitType, + RollupState, + ProofStatus, + Proof, + VerificationKey, + ProvingKey, + CircuitConfig, + AccountState, + Transfer, + Deposit, + Withdrawal, + Batch, + RollupStats, + ProofRequest, + VerificationResult, + MerkleProof, + TrustedSetup, + CeremonyContribution, + ZkConfig, + ZkError, + DEFAULT_CONFIG, + MAX_BATCH_SIZE, + STATE_TREE_DEPTH, +) +from .client import SynorZk + +__version__ = "0.1.0" +__all__ = [ + "SynorZk", + "ProofSystem", + "CircuitType", + "RollupState", + "ProofStatus", + "Proof", + "VerificationKey", + "ProvingKey", + "CircuitConfig", + "AccountState", + "Transfer", + "Deposit", + "Withdrawal", + "Batch", + "RollupStats", + "ProofRequest", + "VerificationResult", + "MerkleProof", + "TrustedSetup", + "CeremonyContribution", + "ZkConfig", + "ZkError", + "DEFAULT_CONFIG", + "MAX_BATCH_SIZE", + "STATE_TREE_DEPTH", +] diff --git a/sdk/python/src/synor_zk/client.py b/sdk/python/src/synor_zk/client.py new file mode 100644 index 0000000..57127cb --- /dev/null +++ b/sdk/python/src/synor_zk/client.py @@ -0,0 +1,407 @@ +""" +Synor ZK SDK Client for Python + +Zero-Knowledge proof systems for ZK-Rollups and privacy. +""" + +import asyncio +import base64 +from typing import Optional, List, Dict, Any + +import httpx + +from .types import ( + ProofSystem, + CircuitType, + Proof, + VerificationKey, + ProvingKey, + CircuitConfig, + AccountState, + Transfer, + Deposit, + Withdrawal, + Batch, + RollupStats, + ProofRequest, + VerificationResult, + MerkleProof, + TrustedSetup, + CeremonyContribution, + ZkConfig, + ZkError, +) + + +class SynorZk: + """ + Synor ZK SDK Client + + Provides zero-knowledge proof generation and verification for ZK-Rollups. + + Example: + ```python + async with SynorZk(ZkConfig(api_key="your-api-key")) as zk: + # Generate a proof + proof = await zk.proofs.generate(ProofRequest( + circuit_type=CircuitType.TRANSFER, + public_inputs=[...], + private_inputs=[...], + )) + + # Verify the proof + result = await zk.proofs.verify(proof) + print(f"Valid: {result.valid}") + ``` + """ + + def __init__(self, config: ZkConfig): + self.config = config + self._client: Optional[httpx.AsyncClient] = None + self._closed = False + + self.proofs = ProofsClient(self) + self.circuits = CircuitsClient(self) + self.rollup = RollupClient(self) + self.state = StateClient(self) + self.ceremony = CeremonyClient(self) + + async def __aenter__(self) -> "SynorZk": + self._client = httpx.AsyncClient(timeout=self.config.timeout) + return self + + async def __aexit__(self, *args) -> None: + await self.close() + + @property + def default_proof_system(self) -> ProofSystem: + """Get the default proof system""" + return self.config.default_proof_system + + async def health_check(self) -> bool: + """Health check""" + try: + result = await self._get("/health") + return result.get("status") == "healthy" + except Exception: + return False + + async def get_info(self) -> Dict[str, Any]: + """Get service info""" + return await self._get("/info") + + async def close(self) -> None: + """Close the client""" + self._closed = True + if self._client: + await self._client.aclose() + self._client = None + + @property + def is_closed(self) -> bool: + """Check if client is closed""" + return self._closed + + async def _get(self, path: str) -> Dict[str, Any]: + """Internal GET request""" + return await self._request("GET", path) + + async def _post(self, path: str, body: Optional[Dict[str, Any]] = None) -> Dict[str, Any]: + """Internal POST request""" + return await self._request("POST", path, body) + + async def _delete(self, path: str) -> Dict[str, Any]: + """Internal DELETE request""" + return await self._request("DELETE", path) + + async def _request( + self, + method: str, + path: str, + body: Optional[Dict[str, Any]] = None, + ) -> Dict[str, Any]: + """Internal HTTP request with retries""" + if self._closed: + raise ZkError("Client has been closed", code="CLIENT_CLOSED") + + if not self._client: + self._client = httpx.AsyncClient(timeout=self.config.timeout) + + url = f"{self.config.endpoint}{path}" + headers = { + "Content-Type": "application/json", + "Authorization": f"Bearer {self.config.api_key}", + "X-SDK-Version": "python/0.1.0", + } + + last_error: Optional[Exception] = None + + for attempt in range(self.config.retries + 1): + try: + if method == "GET": + response = await self._client.get(url, headers=headers) + elif method == "POST": + response = await self._client.post(url, headers=headers, json=body) + elif method == "DELETE": + response = await self._client.delete(url, headers=headers) + else: + raise ValueError(f"Unknown method: {method}") + + if response.status_code >= 400: + error = response.json() if response.content else {} + raise ZkError( + error.get("message", f"HTTP {response.status_code}"), + code=error.get("code"), + status=response.status_code, + ) + + return response.json() + + except ZkError: + raise + except Exception as e: + last_error = e + if attempt < self.config.retries: + await asyncio.sleep(0.1 * (2 ** attempt)) + + raise ZkError( + str(last_error) if last_error else "Request failed", + code="NETWORK_ERROR", + ) + + +class ProofsClient: + """Proofs sub-client""" + + def __init__(self, zk: SynorZk): + self._zk = zk + + async def generate(self, request: ProofRequest) -> Proof: + """Generate a zero-knowledge proof""" + body = request.to_dict() + if not body.get("system"): + body["system"] = self._zk.default_proof_system.value + result = await self._zk._post("/proofs/generate", body) + return Proof.from_dict(result) + + async def verify(self, proof: Proof) -> VerificationResult: + """Verify a proof""" + result = await self._zk._post("/proofs/verify", { + "proof_id": proof.id, + "data": proof.data, + "public_inputs": proof.public_inputs, + "system": proof.system.value, + "circuit_type": proof.circuit_type.value, + }) + return VerificationResult.from_dict(result) + + async def get(self, proof_id: str) -> Proof: + """Get a proof by ID""" + result = await self._zk._get(f"/proofs/{proof_id}") + return Proof.from_dict(result) + + async def list( + self, + limit: Optional[int] = None, + offset: Optional[int] = None, + ) -> List[Proof]: + """List recent proofs""" + params = [] + if limit: + params.append(f"limit={limit}") + if offset: + params.append(f"offset={offset}") + query = f"?{'&'.join(params)}" if params else "" + result = await self._zk._get(f"/proofs{query}") + return [Proof.from_dict(p) for p in result.get("proofs", [])] + + async def serialize(self, proof: Proof) -> bytes: + """Serialize a proof to bytes""" + result = await self._zk._post("/proofs/serialize", {"proof_id": proof.id}) + return base64.b64decode(result["data"]) + + async def deserialize(self, data: bytes, system: ProofSystem) -> Proof: + """Deserialize bytes to a proof""" + result = await self._zk._post("/proofs/deserialize", { + "data": base64.b64encode(data).decode(), + "system": system.value, + }) + return Proof.from_dict(result) + + +class CircuitsClient: + """Circuits sub-client""" + + def __init__(self, zk: SynorZk): + self._zk = zk + + async def get_verification_key( + self, + circuit_type: CircuitType, + system: Optional[ProofSystem] = None, + ) -> VerificationKey: + """Get verification key for a circuit""" + sys = system or self._zk.default_proof_system + result = await self._zk._get(f"/circuits/{circuit_type.value}/vk?system={sys.value}") + return VerificationKey.from_dict(result) + + async def get_proving_key( + self, + circuit_type: CircuitType, + system: Optional[ProofSystem] = None, + ) -> ProvingKey: + """Get proving key for a circuit""" + sys = system or self._zk.default_proof_system + result = await self._zk._get(f"/circuits/{circuit_type.value}/pk?system={sys.value}") + return ProvingKey.from_dict(result) + + async def list(self) -> List[CircuitConfig]: + """List available circuits""" + result = await self._zk._get("/circuits") + return [CircuitConfig.from_dict(c) for c in result.get("circuits", [])] + + async def get_config(self, circuit_type: CircuitType) -> CircuitConfig: + """Get circuit configuration""" + result = await self._zk._get(f"/circuits/{circuit_type.value}/config") + return CircuitConfig.from_dict(result) + + async def compile(self, config: CircuitConfig) -> str: + """Compile a custom circuit, returns circuit ID""" + result = await self._zk._post("/circuits/compile", { + "type": config.type.value, + "max_batch_size": config.max_batch_size, + "tree_depth": config.tree_depth, + "verify_signatures": config.verify_signatures, + "custom_constraints": config.custom_constraints, + }) + return result["circuit_id"] + + +class RollupClient: + """Rollup sub-client""" + + def __init__(self, zk: SynorZk): + self._zk = zk + + async def get_stats(self) -> RollupStats: + """Get rollup statistics""" + result = await self._zk._get("/rollup/stats") + return RollupStats.from_dict(result) + + async def get_current_batch(self) -> Batch: + """Get current batch""" + result = await self._zk._get("/rollup/batch/current") + return Batch.from_dict(result) + + async def get_batch(self, batch_number: int) -> Batch: + """Get batch by number""" + result = await self._zk._get(f"/rollup/batch/{batch_number}") + return Batch.from_dict(result) + + async def submit_transfer(self, transfer: Transfer) -> str: + """Submit a transfer, returns transaction ID""" + result = await self._zk._post("/rollup/transfer", transfer.to_dict()) + return result["tx_id"] + + async def submit_deposit(self, deposit: Deposit) -> str: + """Submit a deposit, returns transaction ID""" + result = await self._zk._post("/rollup/deposit", deposit.to_dict()) + return result["tx_id"] + + async def submit_withdrawal(self, withdrawal: Withdrawal) -> str: + """Submit a withdrawal, returns transaction ID""" + result = await self._zk._post("/rollup/withdraw", withdrawal.to_dict()) + return result["tx_id"] + + async def finalize_batch(self) -> Batch: + """Finalize current batch (generates proof and submits to L1)""" + result = await self._zk._post("/rollup/batch/finalize", {}) + return Batch.from_dict(result) + + async def get_pending_transactions(self) -> List[Transfer]: + """Get pending transactions""" + result = await self._zk._get("/rollup/pending") + return [Transfer.from_dict(t) for t in result.get("transactions", [])] + + +class StateClient: + """State sub-client""" + + def __init__(self, zk: SynorZk): + self._zk = zk + + async def get_root(self) -> str: + """Get current state root""" + result = await self._zk._get("/state/root") + return result["root"] + + async def get_account(self, address: str) -> AccountState: + """Get account state""" + result = await self._zk._get(f"/state/account/{address}") + return AccountState.from_dict(result) + + async def get_merkle_proof(self, address: str) -> MerkleProof: + """Get merkle proof for account inclusion""" + result = await self._zk._get(f"/state/proof/{address}") + return MerkleProof.from_dict(result) + + async def verify_merkle_proof(self, proof: MerkleProof) -> bool: + """Verify merkle proof""" + result = await self._zk._post("/state/proof/verify", proof.to_dict()) + return result["valid"] + + async def get_state_at_batch(self, batch_number: int) -> Dict[str, Any]: + """Get state at specific batch""" + return await self._zk._get(f"/state/batch/{batch_number}") + + +class CeremonyClient: + """Ceremony sub-client (trusted setup)""" + + def __init__(self, zk: SynorZk): + self._zk = zk + + async def get_status( + self, + circuit_type: CircuitType, + system: Optional[ProofSystem] = None, + ) -> TrustedSetup: + """Get ceremony status""" + sys = system or self._zk.default_proof_system + result = await self._zk._get(f"/ceremony/{circuit_type.value}?system={sys.value}") + return TrustedSetup.from_dict(result) + + async def contribute( + self, + circuit_type: CircuitType, + entropy: bytes, + system: Optional[ProofSystem] = None, + ) -> CeremonyContribution: + """Contribute to ceremony""" + sys = system or self._zk.default_proof_system + result = await self._zk._post("/ceremony/contribute", { + "circuit_type": circuit_type.value, + "entropy": base64.b64encode(entropy).decode(), + "system": sys.value, + }) + return CeremonyContribution.from_dict(result) + + async def verify_contribution( + self, + circuit_type: CircuitType, + contribution_hash: str, + ) -> bool: + """Verify a contribution""" + result = await self._zk._post("/ceremony/verify", { + "circuit_type": circuit_type.value, + "contribution_hash": contribution_hash, + }) + return result["valid"] + + async def list_contributions( + self, + circuit_type: CircuitType, + ) -> List[CeremonyContribution]: + """List contributions""" + result = await self._zk._get(f"/ceremony/{circuit_type.value}/contributions") + return [CeremonyContribution.from_dict(c) for c in result.get("contributions", [])] diff --git a/sdk/python/src/synor_zk/types.py b/sdk/python/src/synor_zk/types.py new file mode 100644 index 0000000..df014ea --- /dev/null +++ b/sdk/python/src/synor_zk/types.py @@ -0,0 +1,464 @@ +""" +Synor ZK SDK Types for Python + +Zero-Knowledge proof systems for ZK-Rollups and privacy. +""" + +from dataclasses import dataclass, field +from enum import Enum +from typing import Optional, List, Dict, Any + + +class ProofSystem(str, Enum): + """Proof system backends""" + GROTH16 = "groth16" # Smallest proofs (~192 bytes), fastest verification + PLONK = "plonk" # Universal trusted setup, medium proofs (~512 bytes) + STARK = "stark" # No trusted setup, largest proofs (~50KB) + + +class CircuitType(str, Enum): + """Circuit types for different operations""" + TRANSFER = "transfer" # Single transfer between accounts + BATCH = "batch" # Batch of multiple transfers + DEPOSIT = "deposit" # Deposit from L1 to L2 + WITHDRAW = "withdraw" # Withdrawal from L2 to L1 + + +class RollupState(str, Enum): + """Rollup state""" + ACTIVE = "active" + PAUSED = "paused" + FINALIZING = "finalizing" + FINALIZED = "finalized" + + +class ProofStatus(str, Enum): + """Proof status""" + GENERATING = "generating" + COMPLETED = "completed" + FAILED = "failed" + VERIFIED = "verified" + + +@dataclass +class Proof: + """Zero-knowledge proof""" + id: str + system: ProofSystem + circuit_type: CircuitType + data: str # Base64 encoded + public_inputs: List[str] + size: int + generation_time_ms: int + created_at: int + + @classmethod + def from_dict(cls, data: Dict[str, Any]) -> "Proof": + return cls( + id=data["id"], + system=ProofSystem(data["system"]), + circuit_type=CircuitType(data["circuit_type"]), + data=data["data"], + public_inputs=data.get("public_inputs", []), + size=data.get("size", 0), + generation_time_ms=data.get("generation_time_ms", 0), + created_at=data.get("created_at", 0), + ) + + +@dataclass +class VerificationKey: + """Verification key for a circuit""" + circuit_id: str + circuit_type: CircuitType + system: ProofSystem + data: str # Base64 encoded + size: int + + @classmethod + def from_dict(cls, data: Dict[str, Any]) -> "VerificationKey": + return cls( + circuit_id=data["circuit_id"], + circuit_type=CircuitType(data["circuit_type"]), + system=ProofSystem(data["system"]), + data=data["data"], + size=data.get("size", 0), + ) + + +@dataclass +class ProvingKey: + """Proving key for generating proofs""" + circuit_id: str + circuit_type: CircuitType + system: ProofSystem + data: str # Base64 encoded + size: int + + @classmethod + def from_dict(cls, data: Dict[str, Any]) -> "ProvingKey": + return cls( + circuit_id=data["circuit_id"], + circuit_type=CircuitType(data["circuit_type"]), + system=ProofSystem(data["system"]), + data=data["data"], + size=data.get("size", 0), + ) + + +@dataclass +class CircuitConfig: + """Circuit configuration""" + type: CircuitType + max_batch_size: Optional[int] = None + tree_depth: Optional[int] = None + verify_signatures: Optional[bool] = None + custom_constraints: Optional[Dict[str, Any]] = None + + @classmethod + def from_dict(cls, data: Dict[str, Any]) -> "CircuitConfig": + return cls( + type=CircuitType(data["type"]), + max_batch_size=data.get("max_batch_size"), + tree_depth=data.get("tree_depth"), + verify_signatures=data.get("verify_signatures"), + custom_constraints=data.get("custom_constraints"), + ) + + +@dataclass +class AccountState: + """Account state in the rollup""" + address: str + balance: str + nonce: int + pubkey_hash: str + + @classmethod + def from_dict(cls, data: Dict[str, Any]) -> "AccountState": + return cls( + address=data["address"], + balance=data["balance"], + nonce=data.get("nonce", 0), + pubkey_hash=data.get("pubkey_hash", ""), + ) + + +@dataclass +class Transfer: + """Transfer operation""" + from_addr: str + to: str + amount: str + fee: str + nonce: int + signature: Optional[str] = None + + def to_dict(self) -> Dict[str, Any]: + result = { + "from": self.from_addr, + "to": self.to, + "amount": self.amount, + "fee": self.fee, + "nonce": self.nonce, + } + if self.signature: + result["signature"] = self.signature + return result + + @classmethod + def from_dict(cls, data: Dict[str, Any]) -> "Transfer": + return cls( + from_addr=data["from"], + to=data["to"], + amount=data["amount"], + fee=data["fee"], + nonce=data.get("nonce", 0), + signature=data.get("signature"), + ) + + +@dataclass +class Deposit: + """Deposit operation (L1 -> L2)""" + l1_tx_hash: str + recipient: str + amount: str + token: Optional[str] = None + + def to_dict(self) -> Dict[str, Any]: + result = { + "l1_tx_hash": self.l1_tx_hash, + "recipient": self.recipient, + "amount": self.amount, + } + if self.token: + result["token"] = self.token + return result + + @classmethod + def from_dict(cls, data: Dict[str, Any]) -> "Deposit": + return cls( + l1_tx_hash=data["l1_tx_hash"], + recipient=data["recipient"], + amount=data["amount"], + token=data.get("token"), + ) + + +@dataclass +class Withdrawal: + """Withdrawal operation (L2 -> L1)""" + sender: str + recipient: str + amount: str + token: Optional[str] = None + nonce: int = 0 + signature: Optional[str] = None + + def to_dict(self) -> Dict[str, Any]: + result = { + "sender": self.sender, + "recipient": self.recipient, + "amount": self.amount, + "nonce": self.nonce, + } + if self.token: + result["token"] = self.token + if self.signature: + result["signature"] = self.signature + return result + + @classmethod + def from_dict(cls, data: Dict[str, Any]) -> "Withdrawal": + return cls( + sender=data["sender"], + recipient=data["recipient"], + amount=data["amount"], + token=data.get("token"), + nonce=data.get("nonce", 0), + signature=data.get("signature"), + ) + + +@dataclass +class Batch: + """Rollup batch""" + batch_number: int + prev_state_root: str + new_state_root: str + transfers: List[Transfer] + deposits: List[Deposit] + withdrawals: List[Withdrawal] + proof: Optional[Proof] = None + timestamp: int = 0 + + @classmethod + def from_dict(cls, data: Dict[str, Any]) -> "Batch": + return cls( + batch_number=data["batch_number"], + prev_state_root=data["prev_state_root"], + new_state_root=data["new_state_root"], + transfers=[Transfer.from_dict(t) for t in data.get("transfers", [])], + deposits=[Deposit.from_dict(d) for d in data.get("deposits", [])], + withdrawals=[Withdrawal.from_dict(w) for w in data.get("withdrawals", [])], + proof=Proof.from_dict(data["proof"]) if data.get("proof") else None, + timestamp=data.get("timestamp", 0), + ) + + +@dataclass +class RollupStats: + """Rollup statistics""" + current_batch: int + total_transactions: int + total_proofs: int + avg_proof_time_ms: int + state_root: str + account_count: int + tvl: str + + @classmethod + def from_dict(cls, data: Dict[str, Any]) -> "RollupStats": + return cls( + current_batch=data["current_batch"], + total_transactions=data.get("total_transactions", 0), + total_proofs=data.get("total_proofs", 0), + avg_proof_time_ms=data.get("avg_proof_time_ms", 0), + state_root=data["state_root"], + account_count=data.get("account_count", 0), + tvl=data.get("tvl", "0"), + ) + + +@dataclass +class ProofRequest: + """Proof generation request""" + circuit_type: CircuitType + public_inputs: List[str] + private_inputs: List[str] + system: Optional[ProofSystem] = None + + def to_dict(self) -> Dict[str, Any]: + result = { + "circuit_type": self.circuit_type.value, + "public_inputs": self.public_inputs, + "private_inputs": self.private_inputs, + } + if self.system: + result["system"] = self.system.value + return result + + +@dataclass +class VerificationResult: + """Proof verification result""" + valid: bool + verification_time_ms: int + error: Optional[str] = None + + @classmethod + def from_dict(cls, data: Dict[str, Any]) -> "VerificationResult": + return cls( + valid=data["valid"], + verification_time_ms=data.get("verification_time_ms", 0), + error=data.get("error"), + ) + + +@dataclass +class MerkleProof: + """Merkle proof for state inclusion""" + leaf: str + path: List[str] + indices: List[int] + root: str + + def to_dict(self) -> Dict[str, Any]: + return { + "leaf": self.leaf, + "path": self.path, + "indices": self.indices, + "root": self.root, + } + + @classmethod + def from_dict(cls, data: Dict[str, Any]) -> "MerkleProof": + return cls( + leaf=data["leaf"], + path=data["path"], + indices=data["indices"], + root=data["root"], + ) + + +@dataclass +class CeremonyContribution: + """Trusted setup ceremony contribution""" + contributor_id: str + hash: str + timestamp: int + verified: bool + + @classmethod + def from_dict(cls, data: Dict[str, Any]) -> "CeremonyContribution": + return cls( + contributor_id=data["contributor_id"], + hash=data["hash"], + timestamp=data.get("timestamp", 0), + verified=data.get("verified", False), + ) + + +@dataclass +class TrustedSetup: + """Trusted setup ceremony""" + id: str + circuit_type: CircuitType + system: ProofSystem + contribution_count: int + latest_hash: str + complete: bool + contributions: List[CeremonyContribution] + + @classmethod + def from_dict(cls, data: Dict[str, Any]) -> "TrustedSetup": + return cls( + id=data["id"], + circuit_type=CircuitType(data["circuit_type"]), + system=ProofSystem(data["system"]), + contribution_count=data.get("contribution_count", 0), + latest_hash=data.get("latest_hash", ""), + complete=data.get("complete", False), + contributions=[ + CeremonyContribution.from_dict(c) + for c in data.get("contributions", []) + ], + ) + + +@dataclass +class ZkConfig: + """ZK SDK configuration""" + api_key: str + endpoint: str = "https://zk.synor.io/v1" + ws_endpoint: str = "wss://zk.synor.io/v1/ws" + timeout: int = 60 # Seconds (ZK proofs take longer) + retries: int = 3 + default_proof_system: ProofSystem = ProofSystem.GROTH16 + debug: bool = False + + +class ZkError(Exception): + """ZK SDK error""" + + def __init__( + self, + message: str, + code: Optional[str] = None, + status: Optional[int] = None, + ): + super().__init__(message) + self.code = code + self.status = status + + +# Default configuration +DEFAULT_CONFIG = { + "endpoint": "https://zk.synor.io/v1", + "ws_endpoint": "wss://zk.synor.io/v1/ws", + "timeout": 60, + "retries": 3, + "default_proof_system": ProofSystem.GROTH16, + "debug": False, +} + +# Constants +MAX_BATCH_SIZE = 1000 +STATE_TREE_DEPTH = 32 +PROOF_EXPIRY_BLOCKS = 100 + +# Proof system characteristics +PROOF_SYSTEM_INFO = { + ProofSystem.GROTH16: { + "name": "Groth16", + "proof_size": 192, + "verification_time_ms": 10, + "trusted_setup": True, + "universal": False, + }, + ProofSystem.PLONK: { + "name": "PLONK", + "proof_size": 512, + "verification_time_ms": 15, + "trusted_setup": True, + "universal": True, + }, + ProofSystem.STARK: { + "name": "STARK", + "proof_size": 50000, + "verification_time_ms": 30, + "trusted_setup": False, + "universal": True, + }, +} diff --git a/sdk/ruby/lib/synor/zk.rb b/sdk/ruby/lib/synor/zk.rb new file mode 100644 index 0000000..9509d9b --- /dev/null +++ b/sdk/ruby/lib/synor/zk.rb @@ -0,0 +1,770 @@ +# frozen_string_literal: true + +require 'net/http' +require 'uri' +require 'json' +require 'base64' + +module Synor + # ZK SDK for Ruby + # + # Zero-Knowledge proof systems for ZK-Rollups and privacy. + module Zk + VERSION = '0.1.0' + + # Proof system backends + module ProofSystem + GROTH16 = 'groth16' # Smallest proofs (~192 bytes), fastest verification + PLONK = 'plonk' # Universal trusted setup, medium proofs (~512 bytes) + STARK = 'stark' # No trusted setup, largest proofs (~50KB) + end + + # Circuit types for different operations + module CircuitType + TRANSFER = 'transfer' # Single transfer between accounts + BATCH = 'batch' # Batch of multiple transfers + DEPOSIT = 'deposit' # Deposit from L1 to L2 + WITHDRAW = 'withdraw' # Withdrawal from L2 to L1 + end + + # Rollup state + module RollupState + ACTIVE = 'active' + PAUSED = 'paused' + FINALIZING = 'finalizing' + FINALIZED = 'finalized' + end + + # Proof status + module ProofStatus + GENERATING = 'generating' + COMPLETED = 'completed' + FAILED = 'failed' + VERIFIED = 'verified' + end + + # Zero-knowledge proof + class Proof + attr_reader :id, :system, :circuit_type, :data, :public_inputs, + :size, :generation_time_ms, :created_at + + def initialize(id:, system:, circuit_type:, data:, public_inputs:, + size:, generation_time_ms:, created_at:) + @id = id + @system = system + @circuit_type = circuit_type + @data = data + @public_inputs = public_inputs + @size = size + @generation_time_ms = generation_time_ms + @created_at = created_at + end + + def self.from_json(json) + Proof.new( + id: json['id'], + system: json['system'], + circuit_type: json['circuit_type'], + data: json['data'], + public_inputs: json['public_inputs'] || [], + size: json['size'] || 0, + generation_time_ms: json['generation_time_ms'] || 0, + created_at: json['created_at'] || 0 + ) + end + + def to_json(*_args) + { + 'id' => @id, + 'system' => @system, + 'circuit_type' => @circuit_type, + 'data' => @data, + 'public_inputs' => @public_inputs, + 'size' => @size, + 'generation_time_ms' => @generation_time_ms, + 'created_at' => @created_at + } + end + end + + # Verification key + class VerificationKey + attr_reader :circuit_id, :circuit_type, :system, :data, :size + + def initialize(circuit_id:, circuit_type:, system:, data:, size:) + @circuit_id = circuit_id + @circuit_type = circuit_type + @system = system + @data = data + @size = size + end + + def self.from_json(json) + VerificationKey.new( + circuit_id: json['circuit_id'], + circuit_type: json['circuit_type'], + system: json['system'], + data: json['data'], + size: json['size'] || 0 + ) + end + end + + # Proving key + class ProvingKey + attr_reader :circuit_id, :circuit_type, :system, :data, :size + + def initialize(circuit_id:, circuit_type:, system:, data:, size:) + @circuit_id = circuit_id + @circuit_type = circuit_type + @system = system + @data = data + @size = size + end + + def self.from_json(json) + ProvingKey.new( + circuit_id: json['circuit_id'], + circuit_type: json['circuit_type'], + system: json['system'], + data: json['data'], + size: json['size'] || 0 + ) + end + end + + # Circuit configuration + class CircuitConfig + attr_reader :type, :max_batch_size, :tree_depth, :verify_signatures, :custom_constraints + + def initialize(type:, max_batch_size: nil, tree_depth: nil, + verify_signatures: nil, custom_constraints: nil) + @type = type + @max_batch_size = max_batch_size + @tree_depth = tree_depth + @verify_signatures = verify_signatures + @custom_constraints = custom_constraints + end + + def self.from_json(json) + CircuitConfig.new( + type: json['type'], + max_batch_size: json['max_batch_size'], + tree_depth: json['tree_depth'], + verify_signatures: json['verify_signatures'], + custom_constraints: json['custom_constraints'] + ) + end + + def to_json(*_args) + result = { 'type' => @type } + result['max_batch_size'] = @max_batch_size if @max_batch_size + result['tree_depth'] = @tree_depth if @tree_depth + result['verify_signatures'] = @verify_signatures unless @verify_signatures.nil? + result['custom_constraints'] = @custom_constraints if @custom_constraints + result + end + end + + # Account state + class AccountState + attr_reader :address, :balance, :nonce, :pubkey_hash + + def initialize(address:, balance:, nonce:, pubkey_hash:) + @address = address + @balance = balance + @nonce = nonce + @pubkey_hash = pubkey_hash + end + + def self.from_json(json) + AccountState.new( + address: json['address'], + balance: json['balance'], + nonce: json['nonce'] || 0, + pubkey_hash: json['pubkey_hash'] || '' + ) + end + end + + # Transfer operation + class Transfer + attr_reader :from, :to, :amount, :fee, :nonce, :signature + + def initialize(from:, to:, amount:, fee:, nonce:, signature: nil) + @from = from + @to = to + @amount = amount + @fee = fee + @nonce = nonce + @signature = signature + end + + def self.from_json(json) + Transfer.new( + from: json['from'], + to: json['to'], + amount: json['amount'], + fee: json['fee'], + nonce: json['nonce'] || 0, + signature: json['signature'] + ) + end + + def to_json(*_args) + result = { 'from' => @from, 'to' => @to, 'amount' => @amount, 'fee' => @fee, 'nonce' => @nonce } + result['signature'] = @signature if @signature + result + end + end + + # Deposit operation + class Deposit + attr_reader :l1_tx_hash, :recipient, :amount, :token + + def initialize(l1_tx_hash:, recipient:, amount:, token: nil) + @l1_tx_hash = l1_tx_hash + @recipient = recipient + @amount = amount + @token = token + end + + def to_json(*_args) + result = { 'l1_tx_hash' => @l1_tx_hash, 'recipient' => @recipient, 'amount' => @amount } + result['token'] = @token if @token + result + end + end + + # Withdrawal operation + class Withdrawal + attr_reader :sender, :recipient, :amount, :token, :nonce, :signature + + def initialize(sender:, recipient:, amount:, nonce:, token: nil, signature: nil) + @sender = sender + @recipient = recipient + @amount = amount + @token = token + @nonce = nonce + @signature = signature + end + + def to_json(*_args) + result = { 'sender' => @sender, 'recipient' => @recipient, 'amount' => @amount, 'nonce' => @nonce } + result['token'] = @token if @token + result['signature'] = @signature if @signature + result + end + end + + # Rollup batch + class Batch + attr_reader :batch_number, :prev_state_root, :new_state_root, + :transfers, :deposits, :withdrawals, :proof, :timestamp + + def initialize(batch_number:, prev_state_root:, new_state_root:, transfers:, + deposits:, withdrawals:, proof: nil, timestamp:) + @batch_number = batch_number + @prev_state_root = prev_state_root + @new_state_root = new_state_root + @transfers = transfers + @deposits = deposits + @withdrawals = withdrawals + @proof = proof + @timestamp = timestamp + end + + def self.from_json(json) + Batch.new( + batch_number: json['batch_number'], + prev_state_root: json['prev_state_root'], + new_state_root: json['new_state_root'], + transfers: (json['transfers'] || []).map { |t| Transfer.from_json(t) }, + deposits: json['deposits'] || [], + withdrawals: json['withdrawals'] || [], + proof: json['proof'] ? Proof.from_json(json['proof']) : nil, + timestamp: json['timestamp'] || 0 + ) + end + end + + # Rollup statistics + class RollupStats + attr_reader :current_batch, :total_transactions, :total_proofs, + :avg_proof_time_ms, :state_root, :account_count, :tvl + + def initialize(current_batch:, total_transactions:, total_proofs:, + avg_proof_time_ms:, state_root:, account_count:, tvl:) + @current_batch = current_batch + @total_transactions = total_transactions + @total_proofs = total_proofs + @avg_proof_time_ms = avg_proof_time_ms + @state_root = state_root + @account_count = account_count + @tvl = tvl + end + + def self.from_json(json) + RollupStats.new( + current_batch: json['current_batch'], + total_transactions: json['total_transactions'] || 0, + total_proofs: json['total_proofs'] || 0, + avg_proof_time_ms: json['avg_proof_time_ms'] || 0, + state_root: json['state_root'], + account_count: json['account_count'] || 0, + tvl: json['tvl'] || '0' + ) + end + end + + # Proof request + class ProofRequest + attr_reader :circuit_type, :public_inputs, :private_inputs, :system + + def initialize(circuit_type:, public_inputs:, private_inputs:, system: nil) + @circuit_type = circuit_type + @public_inputs = public_inputs + @private_inputs = private_inputs + @system = system + end + + def to_json(*_args) + result = { + 'circuit_type' => @circuit_type, + 'public_inputs' => @public_inputs, + 'private_inputs' => @private_inputs + } + result['system'] = @system if @system + result + end + end + + # Verification result + class VerificationResult + attr_reader :valid, :verification_time_ms, :error + + def initialize(valid:, verification_time_ms:, error: nil) + @valid = valid + @verification_time_ms = verification_time_ms + @error = error + end + + def self.from_json(json) + VerificationResult.new( + valid: json['valid'], + verification_time_ms: json['verification_time_ms'] || 0, + error: json['error'] + ) + end + end + + # Merkle proof + class MerkleProof + attr_reader :leaf, :path, :indices, :root + + def initialize(leaf:, path:, indices:, root:) + @leaf = leaf + @path = path + @indices = indices + @root = root + end + + def self.from_json(json) + MerkleProof.new( + leaf: json['leaf'], + path: json['path'], + indices: json['indices'], + root: json['root'] + ) + end + + def to_json(*_args) + { 'leaf' => @leaf, 'path' => @path, 'indices' => @indices, 'root' => @root } + end + end + + # Ceremony contribution + class CeremonyContribution + attr_reader :contributor_id, :hash, :timestamp, :verified + + def initialize(contributor_id:, hash:, timestamp:, verified:) + @contributor_id = contributor_id + @hash = hash + @timestamp = timestamp + @verified = verified + end + + def self.from_json(json) + CeremonyContribution.new( + contributor_id: json['contributor_id'], + hash: json['hash'], + timestamp: json['timestamp'] || 0, + verified: json['verified'] || false + ) + end + end + + # Trusted setup ceremony + class TrustedSetup + attr_reader :id, :circuit_type, :system, :contribution_count, + :latest_hash, :complete, :contributions + + def initialize(id:, circuit_type:, system:, contribution_count:, + latest_hash:, complete:, contributions:) + @id = id + @circuit_type = circuit_type + @system = system + @contribution_count = contribution_count + @latest_hash = latest_hash + @complete = complete + @contributions = contributions + end + + def self.from_json(json) + TrustedSetup.new( + id: json['id'], + circuit_type: json['circuit_type'], + system: json['system'], + contribution_count: json['contribution_count'] || 0, + latest_hash: json['latest_hash'] || '', + complete: json['complete'] || false, + contributions: (json['contributions'] || []).map { |c| CeremonyContribution.from_json(c) } + ) + end + end + + # ZK SDK configuration + class Config + attr_accessor :api_key, :endpoint, :ws_endpoint, :timeout, :retries, + :default_proof_system, :debug + + def initialize( + api_key:, + endpoint: 'https://zk.synor.io/v1', + ws_endpoint: 'wss://zk.synor.io/v1/ws', + timeout: 60, + retries: 3, + default_proof_system: ProofSystem::GROTH16, + debug: false + ) + @api_key = api_key + @endpoint = endpoint + @ws_endpoint = ws_endpoint + @timeout = timeout + @retries = retries + @default_proof_system = default_proof_system + @debug = debug + end + end + + # ZK exception + class ZkError < StandardError + attr_reader :code, :status + + def initialize(message, code: nil, status: nil) + super(message) + @code = code + @status = status + end + end + + # Proof system info + ProofSystemInfo = Struct.new(:name, :proof_size, :verification_time_ms, :trusted_setup, :universal) + + PROOF_SYSTEM_INFO = { + ProofSystem::GROTH16 => ProofSystemInfo.new('Groth16', 192, 10, true, false), + ProofSystem::PLONK => ProofSystemInfo.new('PLONK', 512, 15, true, true), + ProofSystem::STARK => ProofSystemInfo.new('STARK', 50_000, 30, false, true) + }.freeze + + # Constants + MAX_BATCH_SIZE = 1000 + STATE_TREE_DEPTH = 32 + PROOF_EXPIRY_BLOCKS = 100 + + # Main ZK client + class Client + attr_reader :proofs, :circuits, :rollup, :state, :ceremony + + def initialize(config) + @config = config + @closed = false + + @proofs = ProofsClient.new(self) + @circuits = CircuitsClient.new(self) + @rollup = RollupClient.new(self) + @state = StateClient.new(self) + @ceremony = CeremonyClient.new(self) + end + + def default_proof_system + @config.default_proof_system + end + + def health_check + result = get('/health') + result['status'] == 'healthy' + rescue StandardError + false + end + + def get_info + get('/info') + end + + def close + @closed = true + end + + def closed? + @closed + end + + def get(path) + request(:get, path) + end + + def post(path, body) + request(:post, path, body) + end + + private + + def request(method, path, body = nil) + raise ZkError.new('Client has been closed', code: 'CLIENT_CLOSED') if @closed + + uri = URI.parse("#{@config.endpoint}#{path}") + + http = Net::HTTP.new(uri.host, uri.port) + http.use_ssl = uri.scheme == 'https' + http.read_timeout = @config.timeout + + request = case method + when :get + Net::HTTP::Get.new(uri.request_uri) + when :post + req = Net::HTTP::Post.new(uri.request_uri) + req.body = body.to_json if body + req + end + + request['Content-Type'] = 'application/json' + request['Authorization'] = "Bearer #{@config.api_key}" + request['X-SDK-Version'] = 'ruby/0.1.0' + + response = http.request(request) + + unless response.is_a?(Net::HTTPSuccess) + error = JSON.parse(response.body) rescue {} + raise ZkError.new( + error['message'] || "HTTP #{response.code}", + code: error['code'], + status: response.code.to_i + ) + end + + JSON.parse(response.body) + end + end + + # Proofs sub-client + class ProofsClient + def initialize(zk) + @zk = zk + end + + def generate(request) + body = request.to_json + body['system'] ||= @zk.default_proof_system + result = @zk.post('/proofs/generate', body) + Proof.from_json(result) + end + + def verify(proof) + result = @zk.post('/proofs/verify', { + 'proof_id' => proof.id, + 'data' => proof.data, + 'public_inputs' => proof.public_inputs, + 'system' => proof.system, + 'circuit_type' => proof.circuit_type + }) + VerificationResult.from_json(result) + end + + def get(proof_id) + result = @zk.get("/proofs/#{proof_id}") + Proof.from_json(result) + end + + def list(limit: nil, offset: nil) + params = [] + params << "limit=#{limit}" if limit + params << "offset=#{offset}" if offset + path = params.empty? ? '/proofs' : "/proofs?#{params.join('&')}" + result = @zk.get(path) + (result['proofs'] || []).map { |p| Proof.from_json(p) } + end + + def serialize(proof) + result = @zk.post('/proofs/serialize', { 'proof_id' => proof.id }) + Base64.strict_decode64(result['data']) + end + + def deserialize(data, system) + result = @zk.post('/proofs/deserialize', { + 'data' => Base64.strict_encode64(data), + 'system' => system + }) + Proof.from_json(result) + end + end + + # Circuits sub-client + class CircuitsClient + def initialize(zk) + @zk = zk + end + + def get_verification_key(circuit_type, system = nil) + sys = system || @zk.default_proof_system + result = @zk.get("/circuits/#{circuit_type}/vk?system=#{sys}") + VerificationKey.from_json(result) + end + + def get_proving_key(circuit_type, system = nil) + sys = system || @zk.default_proof_system + result = @zk.get("/circuits/#{circuit_type}/pk?system=#{sys}") + ProvingKey.from_json(result) + end + + def list + result = @zk.get('/circuits') + (result['circuits'] || []).map { |c| CircuitConfig.from_json(c) } + end + + def get_config(circuit_type) + result = @zk.get("/circuits/#{circuit_type}/config") + CircuitConfig.from_json(result) + end + + def compile(config) + result = @zk.post('/circuits/compile', config.to_json) + result['circuit_id'] + end + end + + # Rollup sub-client + class RollupClient + def initialize(zk) + @zk = zk + end + + def get_stats + result = @zk.get('/rollup/stats') + RollupStats.from_json(result) + end + + def get_current_batch + result = @zk.get('/rollup/batch/current') + Batch.from_json(result) + end + + def get_batch(batch_number) + result = @zk.get("/rollup/batch/#{batch_number}") + Batch.from_json(result) + end + + def submit_transfer(transfer) + result = @zk.post('/rollup/transfer', transfer.to_json) + result['tx_id'] + end + + def submit_deposit(deposit) + result = @zk.post('/rollup/deposit', deposit.to_json) + result['tx_id'] + end + + def submit_withdrawal(withdrawal) + result = @zk.post('/rollup/withdraw', withdrawal.to_json) + result['tx_id'] + end + + def finalize_batch + result = @zk.post('/rollup/batch/finalize', {}) + Batch.from_json(result) + end + + def get_pending_transactions + result = @zk.get('/rollup/pending') + (result['transactions'] || []).map { |t| Transfer.from_json(t) } + end + end + + # State sub-client + class StateClient + def initialize(zk) + @zk = zk + end + + def get_root + result = @zk.get('/state/root') + result['root'] + end + + def get_account(address) + result = @zk.get("/state/account/#{address}") + AccountState.from_json(result) + end + + def get_merkle_proof(address) + result = @zk.get("/state/proof/#{address}") + MerkleProof.from_json(result) + end + + def verify_merkle_proof(proof) + result = @zk.post('/state/proof/verify', proof.to_json) + result['valid'] + end + + def get_state_at_batch(batch_number) + @zk.get("/state/batch/#{batch_number}") + end + end + + # Ceremony sub-client + class CeremonyClient + def initialize(zk) + @zk = zk + end + + def get_status(circuit_type, system = nil) + sys = system || @zk.default_proof_system + result = @zk.get("/ceremony/#{circuit_type}?system=#{sys}") + TrustedSetup.from_json(result) + end + + def contribute(circuit_type, entropy, system = nil) + sys = system || @zk.default_proof_system + result = @zk.post('/ceremony/contribute', { + 'circuit_type' => circuit_type, + 'entropy' => Base64.strict_encode64(entropy.pack('C*')), + 'system' => sys + }) + CeremonyContribution.from_json(result) + end + + def verify_contribution(circuit_type, contribution_hash) + result = @zk.post('/ceremony/verify', { + 'circuit_type' => circuit_type, + 'contribution_hash' => contribution_hash + }) + result['valid'] + end + + def list_contributions(circuit_type) + result = @zk.get("/ceremony/#{circuit_type}/contributions") + (result['contributions'] || []).map { |c| CeremonyContribution.from_json(c) } + end + end + end +end diff --git a/sdk/rust/src/zk/client.rs b/sdk/rust/src/zk/client.rs new file mode 100644 index 0000000..11be1a7 --- /dev/null +++ b/sdk/rust/src/zk/client.rs @@ -0,0 +1,376 @@ +//! ZK SDK Client + +use crate::zk::types::*; +use base64::{Engine as _, engine::general_purpose::STANDARD}; +use reqwest::Client; +use serde::{de::DeserializeOwned, Serialize}; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::Arc; + +/// Synor ZK SDK Client +pub struct SynorZk { + config: ZkConfig, + client: Client, + closed: Arc, +} + +impl SynorZk { + /// Create a new ZK SDK client + pub fn new(config: ZkConfig) -> Self { + Self { + config, + client: Client::new(), + closed: Arc::new(AtomicBool::new(false)), + } + } + + /// Get the default proof system + pub fn default_proof_system(&self) -> ProofSystem { + self.config.default_proof_system + } + + /// Health check + pub async fn health_check(&self) -> bool { + match self.get::("/health").await { + Ok(v) => v.get("status").and_then(|s| s.as_str()) == Some("healthy"), + Err(_) => false, + } + } + + /// Get service info + pub async fn get_info(&self) -> ZkResult { + self.get("/info").await + } + + /// Close the client + pub fn close(&self) { + self.closed.store(true, Ordering::SeqCst); + } + + /// Check if client is closed + pub fn is_closed(&self) -> bool { + self.closed.load(Ordering::SeqCst) + } + + // ======================================================================== + // Proof Operations + // ======================================================================== + + /// Generate a zero-knowledge proof + pub async fn generate_proof(&self, request: ProofRequest) -> ZkResult { + let system = request.system.unwrap_or(self.default_proof_system()); + self.post("/proofs/generate", &serde_json::json!({ + "circuit_type": request.circuit_type, + "public_inputs": request.public_inputs, + "private_inputs": request.private_inputs, + "system": system, + })).await + } + + /// Verify a proof + pub async fn verify_proof(&self, proof: &Proof) -> ZkResult { + self.post("/proofs/verify", &serde_json::json!({ + "proof_id": proof.id, + "data": proof.data, + "public_inputs": proof.public_inputs, + "system": proof.system, + "circuit_type": proof.circuit_type, + })).await + } + + /// Get a proof by ID + pub async fn get_proof(&self, proof_id: &str) -> ZkResult { + self.get(&format!("/proofs/{}", proof_id)).await + } + + /// List recent proofs + pub async fn list_proofs(&self, limit: Option, offset: Option) -> ZkResult> { + let mut path = "/proofs".to_string(); + if limit.is_some() || offset.is_some() { + path = format!("/proofs?limit={}&offset={}", limit.unwrap_or(10), offset.unwrap_or(0)); + } + #[derive(Deserialize)] + struct Response { proofs: Vec } + let resp: Response = self.get(&path).await?; + Ok(resp.proofs) + } + + /// Serialize a proof to bytes + pub async fn serialize_proof(&self, proof: &Proof) -> ZkResult> { + #[derive(Deserialize)] + struct Response { data: String } + let resp: Response = self.post("/proofs/serialize", &serde_json::json!({ + "proof_id": proof.id, + })).await?; + STANDARD.decode(&resp.data).map_err(|e| ZkError { + message: e.to_string(), + code: None, + status: None, + }) + } + + /// Deserialize bytes to a proof + pub async fn deserialize_proof(&self, data: &[u8], system: ProofSystem) -> ZkResult { + self.post("/proofs/deserialize", &serde_json::json!({ + "data": STANDARD.encode(data), + "system": system, + })).await + } + + // ======================================================================== + // Circuit Operations + // ======================================================================== + + /// Get verification key for a circuit + pub async fn get_verification_key(&self, circuit_type: CircuitType, system: Option) -> ZkResult { + let sys = system.unwrap_or(self.default_proof_system()); + self.get(&format!("/circuits/{:?}/vk?system={:?}", circuit_type, sys).to_lowercase()).await + } + + /// Get proving key for a circuit + pub async fn get_proving_key(&self, circuit_type: CircuitType, system: Option) -> ZkResult { + let sys = system.unwrap_or(self.default_proof_system()); + self.get(&format!("/circuits/{:?}/pk?system={:?}", circuit_type, sys).to_lowercase()).await + } + + /// List available circuits + pub async fn list_circuits(&self) -> ZkResult> { + #[derive(Deserialize)] + struct Response { circuits: Vec } + let resp: Response = self.get("/circuits").await?; + Ok(resp.circuits) + } + + /// Get circuit configuration + pub async fn get_circuit_config(&self, circuit_type: CircuitType) -> ZkResult { + self.get(&format!("/circuits/{:?}/config", circuit_type).to_lowercase()).await + } + + /// Compile a custom circuit + pub async fn compile_circuit(&self, config: CircuitConfig) -> ZkResult { + #[derive(Deserialize)] + struct Response { circuit_id: String } + let resp: Response = self.post("/circuits/compile", &config).await?; + Ok(resp.circuit_id) + } + + // ======================================================================== + // Rollup Operations + // ======================================================================== + + /// Get rollup statistics + pub async fn get_rollup_stats(&self) -> ZkResult { + self.get("/rollup/stats").await + } + + /// Get current batch + pub async fn get_current_batch(&self) -> ZkResult { + self.get("/rollup/batch/current").await + } + + /// Get batch by number + pub async fn get_batch(&self, batch_number: u64) -> ZkResult { + self.get(&format!("/rollup/batch/{}", batch_number)).await + } + + /// Submit a transfer + pub async fn submit_transfer(&self, transfer: Transfer) -> ZkResult { + #[derive(Deserialize)] + struct Response { tx_id: String } + let resp: Response = self.post("/rollup/transfer", &transfer).await?; + Ok(resp.tx_id) + } + + /// Submit a deposit + pub async fn submit_deposit(&self, deposit: Deposit) -> ZkResult { + #[derive(Deserialize)] + struct Response { tx_id: String } + let resp: Response = self.post("/rollup/deposit", &deposit).await?; + Ok(resp.tx_id) + } + + /// Submit a withdrawal + pub async fn submit_withdrawal(&self, withdrawal: Withdrawal) -> ZkResult { + #[derive(Deserialize)] + struct Response { tx_id: String } + let resp: Response = self.post("/rollup/withdraw", &withdrawal).await?; + Ok(resp.tx_id) + } + + /// Finalize current batch + pub async fn finalize_batch(&self) -> ZkResult { + self.post("/rollup/batch/finalize", &()).await + } + + /// Get pending transactions + pub async fn get_pending_transactions(&self) -> ZkResult> { + #[derive(Deserialize)] + struct Response { transactions: Vec } + let resp: Response = self.get("/rollup/pending").await?; + Ok(resp.transactions) + } + + // ======================================================================== + // State Operations + // ======================================================================== + + /// Get current state root + pub async fn get_state_root(&self) -> ZkResult { + #[derive(Deserialize)] + struct Response { root: String } + let resp: Response = self.get("/state/root").await?; + Ok(resp.root) + } + + /// Get account state + pub async fn get_account(&self, address: &str) -> ZkResult { + self.get(&format!("/state/account/{}", address)).await + } + + /// Get merkle proof for account inclusion + pub async fn get_merkle_proof(&self, address: &str) -> ZkResult { + self.get(&format!("/state/proof/{}", address)).await + } + + /// Verify merkle proof + pub async fn verify_merkle_proof(&self, proof: &MerkleProof) -> ZkResult { + #[derive(Deserialize)] + struct Response { valid: bool } + let resp: Response = self.post("/state/proof/verify", proof).await?; + Ok(resp.valid) + } + + /// Get state at specific batch + pub async fn get_state_at_batch(&self, batch_number: u64) -> ZkResult { + self.get(&format!("/state/batch/{}", batch_number)).await + } + + // ======================================================================== + // Ceremony Operations + // ======================================================================== + + /// Get ceremony status + pub async fn get_ceremony_status(&self, circuit_type: CircuitType, system: Option) -> ZkResult { + let sys = system.unwrap_or(self.default_proof_system()); + self.get(&format!("/ceremony/{:?}?system={:?}", circuit_type, sys).to_lowercase()).await + } + + /// Contribute to ceremony + pub async fn contribute_to_ceremony( + &self, + circuit_type: CircuitType, + entropy: &[u8], + system: Option, + ) -> ZkResult { + let sys = system.unwrap_or(self.default_proof_system()); + self.post("/ceremony/contribute", &serde_json::json!({ + "circuit_type": circuit_type, + "entropy": STANDARD.encode(entropy), + "system": sys, + })).await + } + + /// Verify a contribution + pub async fn verify_contribution( + &self, + circuit_type: CircuitType, + contribution_hash: &str, + ) -> ZkResult { + #[derive(Deserialize)] + struct Response { valid: bool } + let resp: Response = self.post("/ceremony/verify", &serde_json::json!({ + "circuit_type": circuit_type, + "contribution_hash": contribution_hash, + })).await?; + Ok(resp.valid) + } + + /// List contributions + pub async fn list_contributions(&self, circuit_type: CircuitType) -> ZkResult> { + #[derive(Deserialize)] + struct Response { contributions: Vec } + let resp: Response = self.get(&format!("/ceremony/{:?}/contributions", circuit_type).to_lowercase()).await?; + Ok(resp.contributions) + } + + // ======================================================================== + // HTTP Methods + // ======================================================================== + + async fn get(&self, path: &str) -> ZkResult { + self.request("GET", path, Option::<&()>::None).await + } + + async fn post(&self, path: &str, body: &B) -> ZkResult { + self.request("POST", path, Some(body)).await + } + + async fn request( + &self, + method: &str, + path: &str, + body: Option<&B>, + ) -> ZkResult { + if self.is_closed() { + return Err(ZkError { + message: "Client has been closed".to_string(), + code: Some("CLIENT_CLOSED".to_string()), + status: None, + }); + } + + let url = format!("{}{}", self.config.endpoint, path); + let mut last_error = None; + + for attempt in 0..=self.config.retries { + let mut req = match method { + "GET" => self.client.get(&url), + "POST" => self.client.post(&url), + "DELETE" => self.client.delete(&url), + _ => unreachable!(), + }; + + req = req + .header("Content-Type", "application/json") + .header("Authorization", format!("Bearer {}", self.config.api_key)) + .header("X-SDK-Version", "rust/0.1.0") + .timeout(self.config.timeout); + + if let Some(b) = body { + req = req.json(b); + } + + match req.send().await { + Ok(resp) => { + let status = resp.status(); + if status.is_success() { + return resp.json().await.map_err(|e| ZkError { + message: e.to_string(), + code: None, + status: None, + }); + } else { + let error: serde_json::Value = resp.json().await.unwrap_or_default(); + return Err(ZkError { + message: error["message"].as_str().unwrap_or(&format!("HTTP {}", status)).to_string(), + code: error["code"].as_str().map(String::from), + status: Some(status.as_u16()), + }); + } + } + Err(e) => { + last_error = Some(e.to_string()); + if attempt < self.config.retries { + tokio::time::sleep(std::time::Duration::from_millis(100 * 2u64.pow(attempt))).await; + } + } + } + } + + Err(ZkError { + message: last_error.unwrap_or_else(|| "Request failed".to_string()), + code: Some("NETWORK_ERROR".to_string()), + status: None, + }) + } +} diff --git a/sdk/rust/src/zk/mod.rs b/sdk/rust/src/zk/mod.rs new file mode 100644 index 0000000..8e94e29 --- /dev/null +++ b/sdk/rust/src/zk/mod.rs @@ -0,0 +1,35 @@ +//! Synor ZK SDK for Rust +//! +//! Zero-Knowledge proof systems for ZK-Rollups and privacy. +//! +//! # Example +//! +//! ```rust,no_run +//! use synor_sdk::zk::{SynorZk, ZkConfig, ProofRequest, CircuitType, ProofSystem}; +//! +//! #[tokio::main] +//! async fn main() -> Result<(), Box> { +//! let config = ZkConfig::new("your-api-key"); +//! let zk = SynorZk::new(config); +//! +//! // Generate a proof +//! let proof = zk.generate_proof(ProofRequest { +//! circuit_type: CircuitType::Transfer, +//! public_inputs: vec![], +//! private_inputs: vec![], +//! system: Some(ProofSystem::Groth16), +//! }).await?; +//! +//! // Verify the proof +//! let result = zk.verify_proof(&proof).await?; +//! println!("Valid: {}", result.valid); +//! +//! Ok(()) +//! } +//! ``` + +mod types; +mod client; + +pub use types::*; +pub use client::*; diff --git a/sdk/rust/src/zk/types.rs b/sdk/rust/src/zk/types.rs new file mode 100644 index 0000000..5e97534 --- /dev/null +++ b/sdk/rust/src/zk/types.rs @@ -0,0 +1,312 @@ +//! ZK SDK Types + +use serde::{Deserialize, Serialize}; +use std::fmt; + +/// Proof system backends +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum ProofSystem { + /// Groth16 - smallest proofs (~192 bytes), fastest verification + Groth16, + /// PLONK - universal trusted setup, medium proofs (~512 bytes) + Plonk, + /// STARK - no trusted setup, largest proofs (~50KB) + Stark, +} + +impl Default for ProofSystem { + fn default() -> Self { + Self::Groth16 + } +} + +/// Circuit types for different operations +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum CircuitType { + /// Single transfer between accounts + Transfer, + /// Batch of multiple transfers + Batch, + /// Deposit from L1 to L2 + Deposit, + /// Withdrawal from L2 to L1 + Withdraw, +} + +/// Rollup state +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum RollupState { + Active, + Paused, + Finalizing, + Finalized, +} + +/// Proof status +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum ProofStatus { + Generating, + Completed, + Failed, + Verified, +} + +/// Zero-knowledge proof +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Proof { + pub id: String, + pub system: ProofSystem, + pub circuit_type: CircuitType, + /// Base64 encoded proof data + pub data: String, + pub public_inputs: Vec, + pub size: usize, + pub generation_time_ms: u64, + pub created_at: u64, +} + +/// Verification key for a circuit +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct VerificationKey { + pub circuit_id: String, + pub circuit_type: CircuitType, + pub system: ProofSystem, + /// Base64 encoded verification key + pub data: String, + pub size: usize, +} + +/// Proving key for generating proofs +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ProvingKey { + pub circuit_id: String, + pub circuit_type: CircuitType, + pub system: ProofSystem, + /// Base64 encoded proving key + pub data: String, + pub size: usize, +} + +/// Circuit configuration +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct CircuitConfig { + #[serde(rename = "type")] + pub circuit_type: CircuitType, + #[serde(skip_serializing_if = "Option::is_none")] + pub max_batch_size: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub tree_depth: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub verify_signatures: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub custom_constraints: Option, +} + +/// Account state in the rollup +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct AccountState { + pub address: String, + pub balance: String, + pub nonce: u64, + pub pubkey_hash: String, +} + +/// Transfer operation +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Transfer { + pub from: String, + pub to: String, + pub amount: String, + pub fee: String, + pub nonce: u64, + #[serde(skip_serializing_if = "Option::is_none")] + pub signature: Option, +} + +/// Deposit operation (L1 -> L2) +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Deposit { + pub l1_tx_hash: String, + pub recipient: String, + pub amount: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub token: Option, +} + +/// Withdrawal operation (L2 -> L1) +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Withdrawal { + pub sender: String, + pub recipient: String, + pub amount: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub token: Option, + pub nonce: u64, + #[serde(skip_serializing_if = "Option::is_none")] + pub signature: Option, +} + +/// Rollup batch +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Batch { + pub batch_number: u64, + pub prev_state_root: String, + pub new_state_root: String, + pub transfers: Vec, + pub deposits: Vec, + pub withdrawals: Vec, + #[serde(skip_serializing_if = "Option::is_none")] + pub proof: Option, + pub timestamp: u64, +} + +/// Rollup statistics +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct RollupStats { + pub current_batch: u64, + pub total_transactions: u64, + pub total_proofs: u64, + pub avg_proof_time_ms: u64, + pub state_root: String, + pub account_count: u64, + pub tvl: String, +} + +/// Proof generation request +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ProofRequest { + pub circuit_type: CircuitType, + pub public_inputs: Vec, + pub private_inputs: Vec, + #[serde(skip_serializing_if = "Option::is_none")] + pub system: Option, +} + +/// Proof verification result +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct VerificationResult { + pub valid: bool, + pub verification_time_ms: u64, + #[serde(skip_serializing_if = "Option::is_none")] + pub error: Option, +} + +/// Merkle proof for state inclusion +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct MerkleProof { + pub leaf: String, + pub path: Vec, + pub indices: Vec, + pub root: String, +} + +/// Trusted setup ceremony contribution +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct CeremonyContribution { + pub contributor_id: String, + pub hash: String, + pub timestamp: u64, + pub verified: bool, +} + +/// Trusted setup ceremony +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct TrustedSetup { + pub id: String, + pub circuit_type: CircuitType, + pub system: ProofSystem, + pub contribution_count: usize, + pub latest_hash: String, + pub complete: bool, + pub contributions: Vec, +} + +/// ZK SDK configuration +#[derive(Debug, Clone)] +pub struct ZkConfig { + pub api_key: String, + pub endpoint: String, + pub ws_endpoint: String, + pub timeout: std::time::Duration, + pub retries: u32, + pub default_proof_system: ProofSystem, + pub debug: bool, +} + +impl ZkConfig { + pub fn new(api_key: impl Into) -> Self { + Self { + api_key: api_key.into(), + endpoint: "https://zk.synor.io/v1".to_string(), + ws_endpoint: "wss://zk.synor.io/v1/ws".to_string(), + timeout: std::time::Duration::from_secs(60), + retries: 3, + default_proof_system: ProofSystem::Groth16, + debug: false, + } + } +} + +/// ZK SDK error +#[derive(Debug)] +pub struct ZkError { + pub message: String, + pub code: Option, + pub status: Option, +} + +impl fmt::Display for ZkError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.message) + } +} + +impl std::error::Error for ZkError {} + +pub type ZkResult = Result; + +/// Proof system characteristics +pub struct ProofSystemInfo { + pub name: &'static str, + pub proof_size: usize, + pub verification_time_ms: u64, + pub trusted_setup: bool, + pub universal: bool, +} + +impl ProofSystem { + pub fn info(&self) -> ProofSystemInfo { + match self { + ProofSystem::Groth16 => ProofSystemInfo { + name: "Groth16", + proof_size: 192, + verification_time_ms: 10, + trusted_setup: true, + universal: false, + }, + ProofSystem::Plonk => ProofSystemInfo { + name: "PLONK", + proof_size: 512, + verification_time_ms: 15, + trusted_setup: true, + universal: true, + }, + ProofSystem::Stark => ProofSystemInfo { + name: "STARK", + proof_size: 50000, + verification_time_ms: 30, + trusted_setup: false, + universal: true, + }, + } + } +} + +/// Constants +pub const MAX_BATCH_SIZE: usize = 1000; +pub const STATE_TREE_DEPTH: usize = 32; +pub const PROOF_EXPIRY_BLOCKS: u64 = 100; diff --git a/sdk/swift/Sources/SynorZk/SynorZk.swift b/sdk/swift/Sources/SynorZk/SynorZk.swift new file mode 100644 index 0000000..31baca2 --- /dev/null +++ b/sdk/swift/Sources/SynorZk/SynorZk.swift @@ -0,0 +1,337 @@ +import Foundation + +/// Synor ZK SDK for Swift +/// +/// Zero-Knowledge proof systems for ZK-Rollups and privacy. +public class SynorZk { + private let config: ZkConfig + private let session: URLSession + private var _closed = false + + public lazy var proofs = ProofsClient(zk: self) + public lazy var circuits = CircuitsClient(zk: self) + public lazy var rollup = RollupClient(zk: self) + public lazy var state = StateClient(zk: self) + public lazy var ceremony = CeremonyClient(zk: self) + + public init(config: ZkConfig) { + self.config = config + let sessionConfig = URLSessionConfiguration.default + sessionConfig.timeoutIntervalForRequest = TimeInterval(config.timeout) + self.session = URLSession(configuration: sessionConfig) + } + + public var defaultProofSystem: ProofSystem { config.defaultProofSystem } + + public func healthCheck() async -> Bool { + do { + let result = try await get("/health") + return result["status"] as? String == "healthy" + } catch { + return false + } + } + + public func getInfo() async throws -> [String: Any] { + try await get("/info") + } + + public func close() { + _closed = true + session.invalidateAndCancel() + } + + public var isClosed: Bool { _closed } + + // Internal HTTP methods + func get(_ path: String) async throws -> [String: Any] { + try checkClosed() + var request = URLRequest(url: URL(string: "\(config.endpoint)\(path)")!) + request.httpMethod = "GET" + addHeaders(&request) + return try await performRequest(request) + } + + func post(_ path: String, body: [String: Any]) async throws -> [String: Any] { + try checkClosed() + var request = URLRequest(url: URL(string: "\(config.endpoint)\(path)")!) + request.httpMethod = "POST" + request.httpBody = try JSONSerialization.data(withJSONObject: body) + addHeaders(&request) + return try await performRequest(request) + } + + private func addHeaders(_ request: inout URLRequest) { + request.setValue("application/json", forHTTPHeaderField: "Content-Type") + request.setValue("Bearer \(config.apiKey)", forHTTPHeaderField: "Authorization") + request.setValue("swift/0.1.0", forHTTPHeaderField: "X-SDK-Version") + } + + private func performRequest(_ request: URLRequest) async throws -> [String: Any] { + let (data, response) = try await session.data(for: request) + guard let httpResponse = response as? HTTPURLResponse else { + throw ZkError("Invalid response") + } + if httpResponse.statusCode >= 400 { + let error = try? JSONSerialization.jsonObject(with: data) as? [String: Any] + throw ZkError( + error?["message"] as? String ?? "HTTP \(httpResponse.statusCode)", + code: error?["code"] as? String, + status: httpResponse.statusCode + ) + } + guard let json = try JSONSerialization.jsonObject(with: data) as? [String: Any] else { + throw ZkError("Invalid JSON response") + } + return json + } + + private func checkClosed() throws { + if _closed { + throw ZkError("Client has been closed", code: "CLIENT_CLOSED") + } + } + + /// Proofs sub-client + public class ProofsClient { + private let zk: SynorZk + + init(zk: SynorZk) { + self.zk = zk + } + + public func generate(_ request: ProofRequest) async throws -> Proof { + var body: [String: Any] = [ + "circuit_type": request.circuitType.rawValue, + "public_inputs": request.publicInputs, + "private_inputs": request.privateInputs, + "system": (request.system ?? zk.defaultProofSystem).rawValue + ] + let result = try await zk.post("/proofs/generate", body: body) + return try Proof.fromJson(result) + } + + public func verify(_ proof: Proof) async throws -> VerificationResult { + let body: [String: Any] = [ + "proof_id": proof.id, + "data": proof.data, + "public_inputs": proof.publicInputs, + "system": proof.system.rawValue, + "circuit_type": proof.circuitType.rawValue + ] + let result = try await zk.post("/proofs/verify", body: body) + return VerificationResult.fromJson(result) + } + + public func get(proofId: String) async throws -> Proof { + let result = try await zk.get("/proofs/\(proofId)") + return try Proof.fromJson(result) + } + + public func list(limit: Int? = nil, offset: Int? = nil) async throws -> [Proof] { + var path = "/proofs" + var params: [String] = [] + if let limit = limit { params.append("limit=\(limit)") } + if let offset = offset { params.append("offset=\(offset)") } + if !params.isEmpty { path += "?\(params.joined(separator: "&"))" } + + let result = try await zk.get(path) + guard let proofs = result["proofs"] as? [[String: Any]] else { return [] } + return try proofs.map { try Proof.fromJson($0) } + } + + public func serialize(_ proof: Proof) async throws -> Data { + let result = try await zk.post("/proofs/serialize", body: ["proof_id": proof.id]) + guard let dataStr = result["data"] as? String, + let data = Data(base64Encoded: dataStr) else { + throw ZkError("Invalid response") + } + return data + } + + public func deserialize(_ data: Data, system: ProofSystem) async throws -> Proof { + let result = try await zk.post("/proofs/deserialize", body: [ + "data": data.base64EncodedString(), + "system": system.rawValue + ]) + return try Proof.fromJson(result) + } + } + + /// Circuits sub-client + public class CircuitsClient { + private let zk: SynorZk + + init(zk: SynorZk) { + self.zk = zk + } + + public func getVerificationKey(circuitType: CircuitType, system: ProofSystem? = nil) async throws -> VerificationKey { + let sys = system ?? zk.defaultProofSystem + let result = try await zk.get("/circuits/\(circuitType.rawValue)/vk?system=\(sys.rawValue)") + return try VerificationKey.fromJson(result) + } + + public func getProvingKey(circuitType: CircuitType, system: ProofSystem? = nil) async throws -> ProvingKey { + let sys = system ?? zk.defaultProofSystem + let result = try await zk.get("/circuits/\(circuitType.rawValue)/pk?system=\(sys.rawValue)") + return try ProvingKey.fromJson(result) + } + + public func list() async throws -> [CircuitConfig] { + let result = try await zk.get("/circuits") + guard let circuits = result["circuits"] as? [[String: Any]] else { return [] } + return circuits.compactMap { CircuitConfig.fromJson($0) } + } + + public func getConfig(circuitType: CircuitType) async throws -> CircuitConfig { + let result = try await zk.get("/circuits/\(circuitType.rawValue)/config") + guard let config = CircuitConfig.fromJson(result) else { + throw ZkError("Invalid response") + } + return config + } + + public func compile(_ config: CircuitConfig) async throws -> String { + let result = try await zk.post("/circuits/compile", body: config.toJson()) + guard let circuitId = result["circuit_id"] as? String else { + throw ZkError("Invalid response") + } + return circuitId + } + } + + /// Rollup sub-client + public class RollupClient { + private let zk: SynorZk + + init(zk: SynorZk) { + self.zk = zk + } + + public func getStats() async throws -> RollupStats { + let result = try await zk.get("/rollup/stats") + return RollupStats.fromJson(result) + } + + public func getCurrentBatch() async throws -> Batch { + let result = try await zk.get("/rollup/batch/current") + return try Batch.fromJson(result) + } + + public func getBatch(_ batchNumber: Int) async throws -> Batch { + let result = try await zk.get("/rollup/batch/\(batchNumber)") + return try Batch.fromJson(result) + } + + public func submitTransfer(_ transfer: Transfer) async throws -> String { + let result = try await zk.post("/rollup/transfer", body: transfer.toJson()) + guard let txId = result["tx_id"] as? String else { + throw ZkError("Invalid response") + } + return txId + } + + public func submitDeposit(_ deposit: Deposit) async throws -> String { + let result = try await zk.post("/rollup/deposit", body: deposit.toJson()) + guard let txId = result["tx_id"] as? String else { + throw ZkError("Invalid response") + } + return txId + } + + public func submitWithdrawal(_ withdrawal: Withdrawal) async throws -> String { + let result = try await zk.post("/rollup/withdraw", body: withdrawal.toJson()) + guard let txId = result["tx_id"] as? String else { + throw ZkError("Invalid response") + } + return txId + } + + public func finalizeBatch() async throws -> Batch { + let result = try await zk.post("/rollup/batch/finalize", body: [:]) + return try Batch.fromJson(result) + } + + public func getPendingTransactions() async throws -> [Transfer] { + let result = try await zk.get("/rollup/pending") + guard let transactions = result["transactions"] as? [[String: Any]] else { return [] } + return transactions.compactMap { Transfer.fromJson($0) } + } + } + + /// State sub-client + public class StateClient { + private let zk: SynorZk + + init(zk: SynorZk) { + self.zk = zk + } + + public func getRoot() async throws -> String { + let result = try await zk.get("/state/root") + guard let root = result["root"] as? String else { + throw ZkError("Invalid response") + } + return root + } + + public func getAccount(address: String) async throws -> AccountState { + let result = try await zk.get("/state/account/\(address)") + return AccountState.fromJson(result) + } + + public func getMerkleProof(address: String) async throws -> MerkleProof { + let result = try await zk.get("/state/proof/\(address)") + return MerkleProof.fromJson(result) + } + + public func verifyMerkleProof(_ proof: MerkleProof) async throws -> Bool { + let result = try await zk.post("/state/proof/verify", body: proof.toJson()) + return result["valid"] as? Bool ?? false + } + + public func getStateAtBatch(_ batchNumber: Int) async throws -> [String: Any] { + try await zk.get("/state/batch/\(batchNumber)") + } + } + + /// Ceremony sub-client + public class CeremonyClient { + private let zk: SynorZk + + init(zk: SynorZk) { + self.zk = zk + } + + public func getStatus(circuitType: CircuitType, system: ProofSystem? = nil) async throws -> TrustedSetup { + let sys = system ?? zk.defaultProofSystem + let result = try await zk.get("/ceremony/\(circuitType.rawValue)?system=\(sys.rawValue)") + return TrustedSetup.fromJson(result) + } + + public func contribute(circuitType: CircuitType, entropy: Data, system: ProofSystem? = nil) async throws -> CeremonyContribution { + let sys = system ?? zk.defaultProofSystem + let result = try await zk.post("/ceremony/contribute", body: [ + "circuit_type": circuitType.rawValue, + "entropy": entropy.base64EncodedString(), + "system": sys.rawValue + ]) + return CeremonyContribution.fromJson(result) + } + + public func verifyContribution(circuitType: CircuitType, contributionHash: String) async throws -> Bool { + let result = try await zk.post("/ceremony/verify", body: [ + "circuit_type": circuitType.rawValue, + "contribution_hash": contributionHash + ]) + return result["valid"] as? Bool ?? false + } + + public func listContributions(circuitType: CircuitType) async throws -> [CeremonyContribution] { + let result = try await zk.get("/ceremony/\(circuitType.rawValue)/contributions") + guard let contributions = result["contributions"] as? [[String: Any]] else { return [] } + return contributions.map { CeremonyContribution.fromJson($0) } + } + } +} diff --git a/sdk/swift/Sources/SynorZk/Types.swift b/sdk/swift/Sources/SynorZk/Types.swift new file mode 100644 index 0000000..c830533 --- /dev/null +++ b/sdk/swift/Sources/SynorZk/Types.swift @@ -0,0 +1,496 @@ +import Foundation + +/// Synor ZK SDK Types for Swift +/// +/// Zero-Knowledge proof systems for ZK-Rollups and privacy. + +/// Proof system backends +public enum ProofSystem: String, Codable { + case groth16 + case plonk + case stark +} + +/// Circuit types for different operations +public enum CircuitType: String, Codable { + case transfer + case batch + case deposit + case withdraw +} + +/// Rollup state +public enum RollupState: String, Codable { + case active + case paused + case finalizing + case finalized +} + +/// Proof status +public enum ProofStatus: String, Codable { + case generating + case completed + case failed + case verified +} + +/// Zero-knowledge proof +public struct Proof { + public let id: String + public let system: ProofSystem + public let circuitType: CircuitType + public let data: String + public let publicInputs: [String] + public let size: Int + public let generationTimeMs: Int + public let createdAt: Int + + public init(id: String, system: ProofSystem, circuitType: CircuitType, data: String, + publicInputs: [String], size: Int, generationTimeMs: Int, createdAt: Int) { + self.id = id + self.system = system + self.circuitType = circuitType + self.data = data + self.publicInputs = publicInputs + self.size = size + self.generationTimeMs = generationTimeMs + self.createdAt = createdAt + } + + public static func fromJson(_ json: [String: Any]) throws -> Proof { + guard let id = json["id"] as? String, + let systemStr = json["system"] as? String, + let system = ProofSystem(rawValue: systemStr), + let circuitTypeStr = json["circuit_type"] as? String, + let circuitType = CircuitType(rawValue: circuitTypeStr), + let data = json["data"] as? String else { + throw ZkError("Invalid proof JSON") + } + return Proof( + id: id, + system: system, + circuitType: circuitType, + data: data, + publicInputs: json["public_inputs"] as? [String] ?? [], + size: json["size"] as? Int ?? 0, + generationTimeMs: json["generation_time_ms"] as? Int ?? 0, + createdAt: json["created_at"] as? Int ?? 0 + ) + } +} + +/// Verification key for a circuit +public struct VerificationKey { + public let circuitId: String + public let circuitType: CircuitType + public let system: ProofSystem + public let data: String + public let size: Int + + public static func fromJson(_ json: [String: Any]) throws -> VerificationKey { + guard let circuitId = json["circuit_id"] as? String, + let circuitTypeStr = json["circuit_type"] as? String, + let circuitType = CircuitType(rawValue: circuitTypeStr), + let systemStr = json["system"] as? String, + let system = ProofSystem(rawValue: systemStr), + let data = json["data"] as? String else { + throw ZkError("Invalid verification key JSON") + } + return VerificationKey( + circuitId: circuitId, + circuitType: circuitType, + system: system, + data: data, + size: json["size"] as? Int ?? 0 + ) + } +} + +/// Proving key for generating proofs +public struct ProvingKey { + public let circuitId: String + public let circuitType: CircuitType + public let system: ProofSystem + public let data: String + public let size: Int + + public static func fromJson(_ json: [String: Any]) throws -> ProvingKey { + guard let circuitId = json["circuit_id"] as? String, + let circuitTypeStr = json["circuit_type"] as? String, + let circuitType = CircuitType(rawValue: circuitTypeStr), + let systemStr = json["system"] as? String, + let system = ProofSystem(rawValue: systemStr), + let data = json["data"] as? String else { + throw ZkError("Invalid proving key JSON") + } + return ProvingKey( + circuitId: circuitId, + circuitType: circuitType, + system: system, + data: data, + size: json["size"] as? Int ?? 0 + ) + } +} + +/// Circuit configuration +public struct CircuitConfig { + public let type: CircuitType + public let maxBatchSize: Int? + public let treeDepth: Int? + public let verifySignatures: Bool? + public let customConstraints: [String: Any]? + + public static func fromJson(_ json: [String: Any]) -> CircuitConfig? { + guard let typeStr = json["type"] as? String, + let type = CircuitType(rawValue: typeStr) else { return nil } + return CircuitConfig( + type: type, + maxBatchSize: json["max_batch_size"] as? Int, + treeDepth: json["tree_depth"] as? Int, + verifySignatures: json["verify_signatures"] as? Bool, + customConstraints: json["custom_constraints"] as? [String: Any] + ) + } + + public func toJson() -> [String: Any] { + var result: [String: Any] = ["type": type.rawValue] + if let v = maxBatchSize { result["max_batch_size"] = v } + if let v = treeDepth { result["tree_depth"] = v } + if let v = verifySignatures { result["verify_signatures"] = v } + if let v = customConstraints { result["custom_constraints"] = v } + return result + } +} + +/// Account state in the rollup +public struct AccountState { + public let address: String + public let balance: String + public let nonce: Int + public let pubkeyHash: String + + public static func fromJson(_ json: [String: Any]) -> AccountState { + AccountState( + address: json["address"] as? String ?? "", + balance: json["balance"] as? String ?? "0", + nonce: json["nonce"] as? Int ?? 0, + pubkeyHash: json["pubkey_hash"] as? String ?? "" + ) + } +} + +/// Transfer operation +public struct Transfer { + public let from: String + public let to: String + public let amount: String + public let fee: String + public let nonce: Int + public let signature: String? + + public init(from: String, to: String, amount: String, fee: String, nonce: Int, signature: String? = nil) { + self.from = from + self.to = to + self.amount = amount + self.fee = fee + self.nonce = nonce + self.signature = signature + } + + public static func fromJson(_ json: [String: Any]) -> Transfer? { + guard let from = json["from"] as? String, + let to = json["to"] as? String, + let amount = json["amount"] as? String, + let fee = json["fee"] as? String else { return nil } + return Transfer( + from: from, + to: to, + amount: amount, + fee: fee, + nonce: json["nonce"] as? Int ?? 0, + signature: json["signature"] as? String + ) + } + + public func toJson() -> [String: Any] { + var result: [String: Any] = [ + "from": from, + "to": to, + "amount": amount, + "fee": fee, + "nonce": nonce + ] + if let sig = signature { result["signature"] = sig } + return result + } +} + +/// Deposit operation (L1 -> L2) +public struct Deposit { + public let l1TxHash: String + public let recipient: String + public let amount: String + public let token: String? + + public init(l1TxHash: String, recipient: String, amount: String, token: String? = nil) { + self.l1TxHash = l1TxHash + self.recipient = recipient + self.amount = amount + self.token = token + } + + public func toJson() -> [String: Any] { + var result: [String: Any] = [ + "l1_tx_hash": l1TxHash, + "recipient": recipient, + "amount": amount + ] + if let t = token { result["token"] = t } + return result + } +} + +/// Withdrawal operation (L2 -> L1) +public struct Withdrawal { + public let sender: String + public let recipient: String + public let amount: String + public let token: String? + public let nonce: Int + public let signature: String? + + public init(sender: String, recipient: String, amount: String, token: String? = nil, nonce: Int, signature: String? = nil) { + self.sender = sender + self.recipient = recipient + self.amount = amount + self.token = token + self.nonce = nonce + self.signature = signature + } + + public func toJson() -> [String: Any] { + var result: [String: Any] = [ + "sender": sender, + "recipient": recipient, + "amount": amount, + "nonce": nonce + ] + if let t = token { result["token"] = t } + if let sig = signature { result["signature"] = sig } + return result + } +} + +/// Rollup batch +public struct Batch { + public let batchNumber: Int + public let prevStateRoot: String + public let newStateRoot: String + public let transfers: [Transfer] + public let deposits: [Deposit] + public let withdrawals: [Withdrawal] + public let proof: Proof? + public let timestamp: Int + + public static func fromJson(_ json: [String: Any]) throws -> Batch { + let transfers = (json["transfers"] as? [[String: Any]] ?? []).compactMap { Transfer.fromJson($0) } + return Batch( + batchNumber: json["batch_number"] as? Int ?? 0, + prevStateRoot: json["prev_state_root"] as? String ?? "", + newStateRoot: json["new_state_root"] as? String ?? "", + transfers: transfers, + deposits: [], + withdrawals: [], + proof: (json["proof"] as? [String: Any]).flatMap { try? Proof.fromJson($0) }, + timestamp: json["timestamp"] as? Int ?? 0 + ) + } +} + +/// Rollup statistics +public struct RollupStats { + public let currentBatch: Int + public let totalTransactions: Int + public let totalProofs: Int + public let avgProofTimeMs: Int + public let stateRoot: String + public let accountCount: Int + public let tvl: String + + public static func fromJson(_ json: [String: Any]) -> RollupStats { + RollupStats( + currentBatch: json["current_batch"] as? Int ?? 0, + totalTransactions: json["total_transactions"] as? Int ?? 0, + totalProofs: json["total_proofs"] as? Int ?? 0, + avgProofTimeMs: json["avg_proof_time_ms"] as? Int ?? 0, + stateRoot: json["state_root"] as? String ?? "", + accountCount: json["account_count"] as? Int ?? 0, + tvl: json["tvl"] as? String ?? "0" + ) + } +} + +/// Proof generation request +public struct ProofRequest { + public let circuitType: CircuitType + public let publicInputs: [String] + public let privateInputs: [String] + public let system: ProofSystem? + + public init(circuitType: CircuitType, publicInputs: [String], privateInputs: [String], system: ProofSystem? = nil) { + self.circuitType = circuitType + self.publicInputs = publicInputs + self.privateInputs = privateInputs + self.system = system + } +} + +/// Proof verification result +public struct VerificationResult { + public let valid: Bool + public let verificationTimeMs: Int + public let error: String? + + public static func fromJson(_ json: [String: Any]) -> VerificationResult { + VerificationResult( + valid: json["valid"] as? Bool ?? false, + verificationTimeMs: json["verification_time_ms"] as? Int ?? 0, + error: json["error"] as? String + ) + } +} + +/// Merkle proof for state inclusion +public struct MerkleProof { + public let leaf: String + public let path: [String] + public let indices: [Int] + public let root: String + + public static func fromJson(_ json: [String: Any]) -> MerkleProof { + MerkleProof( + leaf: json["leaf"] as? String ?? "", + path: json["path"] as? [String] ?? [], + indices: json["indices"] as? [Int] ?? [], + root: json["root"] as? String ?? "" + ) + } + + public func toJson() -> [String: Any] { + ["leaf": leaf, "path": path, "indices": indices, "root": root] + } +} + +/// Trusted setup ceremony contribution +public struct CeremonyContribution { + public let contributorId: String + public let hash: String + public let timestamp: Int + public let verified: Bool + + public static func fromJson(_ json: [String: Any]) -> CeremonyContribution { + CeremonyContribution( + contributorId: json["contributor_id"] as? String ?? "", + hash: json["hash"] as? String ?? "", + timestamp: json["timestamp"] as? Int ?? 0, + verified: json["verified"] as? Bool ?? false + ) + } +} + +/// Trusted setup ceremony +public struct TrustedSetup { + public let id: String + public let circuitType: CircuitType + public let system: ProofSystem + public let contributionCount: Int + public let latestHash: String + public let complete: Bool + public let contributions: [CeremonyContribution] + + public static func fromJson(_ json: [String: Any]) -> TrustedSetup { + let contributions = (json["contributions"] as? [[String: Any]] ?? []).map { CeremonyContribution.fromJson($0) } + return TrustedSetup( + id: json["id"] as? String ?? "", + circuitType: CircuitType(rawValue: json["circuit_type"] as? String ?? "transfer") ?? .transfer, + system: ProofSystem(rawValue: json["system"] as? String ?? "groth16") ?? .groth16, + contributionCount: json["contribution_count"] as? Int ?? 0, + latestHash: json["latest_hash"] as? String ?? "", + complete: json["complete"] as? Bool ?? false, + contributions: contributions + ) + } +} + +/// ZK SDK configuration +public struct ZkConfig { + public let apiKey: String + public let endpoint: String + public let wsEndpoint: String + public let timeout: Int + public let retries: Int + public let defaultProofSystem: ProofSystem + public let debug: Bool + + public init( + apiKey: String, + endpoint: String = "https://zk.synor.io/v1", + wsEndpoint: String = "wss://zk.synor.io/v1/ws", + timeout: Int = 60, + retries: Int = 3, + defaultProofSystem: ProofSystem = .groth16, + debug: Bool = false + ) { + self.apiKey = apiKey + self.endpoint = endpoint + self.wsEndpoint = wsEndpoint + self.timeout = timeout + self.retries = retries + self.defaultProofSystem = defaultProofSystem + self.debug = debug + } +} + +/// ZK SDK error +public struct ZkError: Error, CustomStringConvertible { + public let message: String + public let code: String? + public let status: Int? + + public init(_ message: String, code: String? = nil, status: Int? = nil) { + self.message = message + self.code = code + self.status = status + } + + public var description: String { + "ZkError: \(message)\(code.map { " (\($0))" } ?? "")" + } +} + +/// Proof system characteristics +public struct ProofSystemInfo { + public let name: String + public let proofSize: Int + public let verificationTimeMs: Int + public let trustedSetup: Bool + public let universal: Bool +} + +public func getProofSystemInfo(_ system: ProofSystem) -> ProofSystemInfo { + switch system { + case .groth16: + return ProofSystemInfo(name: "Groth16", proofSize: 192, verificationTimeMs: 10, trustedSetup: true, universal: false) + case .plonk: + return ProofSystemInfo(name: "PLONK", proofSize: 512, verificationTimeMs: 15, trustedSetup: true, universal: true) + case .stark: + return ProofSystemInfo(name: "STARK", proofSize: 50000, verificationTimeMs: 30, trustedSetup: false, universal: true) + } +} + +// Constants +public let MAX_BATCH_SIZE = 1000 +public let STATE_TREE_DEPTH = 32 +public let PROOF_EXPIRY_BLOCKS = 100