diff --git a/antd-go/README.md b/antd-go/README.md index bef16f0..934b646 100644 --- a/antd-go/README.md +++ b/antd-go/README.md @@ -30,7 +30,8 @@ func main() { if err != nil { log.Fatal(err) } - fmt.Printf("OK: %v, Network: %s\n", health.OK, health.Network) + fmt.Printf("OK: %v, Network: %s, Version: %s, EVM: %s\n", + health.OK, health.Network, health.Version, health.EvmNetwork) // Store data result, err := client.DataPutPublic(ctx, []byte("Hello, Autonomi!")) @@ -88,7 +89,7 @@ All methods take a `context.Context` as the first parameter for cancellation and ### Health | Method | Description | |--------|-------------| -| `Health(ctx)` | Check daemon status | +| `Health(ctx)` | Check daemon status — returns `*HealthStatus` with daemon version, EVM network, uptime, build commit, and payment contract addresses | ### Data (Immutable) | Method | Description | @@ -167,7 +168,8 @@ func main() { if err != nil { log.Fatal(err) } - fmt.Printf("OK: %v, Network: %s\n", health.OK, health.Network) + fmt.Printf("OK: %v, Network: %s, Version: %s, EVM: %s\n", + health.OK, health.Network, health.Version, health.EvmNetwork) result, err := client.DataPutPublic(ctx, []byte("Hello via gRPC!")) if err != nil { diff --git a/antd-go/client.go b/antd-go/client.go index ac79189..e5a8d80 100644 --- a/antd-go/client.go +++ b/antd-go/client.go @@ -167,8 +167,14 @@ func (c *Client) Health(ctx context.Context) (*HealthStatus, error) { return nil, err } return &HealthStatus{ - OK: str(j, "status") == "ok", - Network: str(j, "network"), + OK: str(j, "status") == "ok", + Network: str(j, "network"), + Version: str(j, "version"), + EvmNetwork: str(j, "evm_network"), + UptimeSeconds: unum64(j, "uptime_seconds"), + BuildCommit: str(j, "build_commit"), + PaymentTokenAddress: str(j, "payment_token_address"), + PaymentVaultAddress: str(j, "payment_vault_address"), }, nil } diff --git a/antd-go/client_test.go b/antd-go/client_test.go index 4159c8e..f1b4090 100644 --- a/antd-go/client_test.go +++ b/antd-go/client_test.go @@ -19,7 +19,16 @@ func mockDaemon(t *testing.T) *httptest.Server { switch { // Health case r.Method == "GET" && r.URL.Path == "/health": - json.NewEncoder(w).Encode(map[string]any{"status": "ok", "network": "local"}) + json.NewEncoder(w).Encode(map[string]any{ + "status": "ok", + "network": "local", + "version": "0.4.0", + "evm_network": "local", + "uptime_seconds": 42, + "build_commit": "abcdef123456", + "payment_token_address": "0xtoken", + "payment_vault_address": "0xvault", + }) // Data put public case r.Method == "POST" && r.URL.Path == "/v1/data/public": @@ -142,6 +151,12 @@ func TestHealth(t *testing.T) { if !h.OK || h.Network != "local" { t.Fatalf("unexpected health: %+v", h) } + if h.Version != "0.4.0" || h.EvmNetwork != "local" || h.UptimeSeconds != 42 { + t.Fatalf("unexpected diagnostic fields: %+v", h) + } + if h.BuildCommit != "abcdef123456" || h.PaymentTokenAddress != "0xtoken" || h.PaymentVaultAddress != "0xvault" { + t.Fatalf("unexpected build/payment fields: %+v", h) + } } func TestDataPublic(t *testing.T) { diff --git a/antd-go/grpc_client.go b/antd-go/grpc_client.go index af715fb..18140e5 100644 --- a/antd-go/grpc_client.go +++ b/antd-go/grpc_client.go @@ -155,8 +155,14 @@ func (c *GrpcClient) Health(ctx context.Context) (*HealthStatus, error) { return nil, errorFromGrpc(err) } return &HealthStatus{ - OK: resp.GetStatus() == "ok", - Network: resp.GetNetwork(), + OK: resp.GetStatus() == "ok", + Network: resp.GetNetwork(), + Version: resp.GetVersion(), + EvmNetwork: resp.GetEvmNetwork(), + UptimeSeconds: resp.GetUptimeSeconds(), + BuildCommit: resp.GetBuildCommit(), + PaymentTokenAddress: resp.GetPaymentTokenAddress(), + PaymentVaultAddress: resp.GetPaymentVaultAddress(), }, nil } diff --git a/antd-go/grpc_client_test.go b/antd-go/grpc_client_test.go index 8a32f68..a9e8f34 100644 --- a/antd-go/grpc_client_test.go +++ b/antd-go/grpc_client_test.go @@ -26,8 +26,14 @@ type mockHealthService struct { func (m *mockHealthService) Check(_ context.Context, _ *pb.HealthCheckRequest) (*pb.HealthCheckResponse, error) { return &pb.HealthCheckResponse{ - Status: "ok", - Network: "local", + Status: "ok", + Network: "local", + Version: "0.4.0", + EvmNetwork: "local", + UptimeSeconds: 42, + BuildCommit: "abcdef123456", + PaymentTokenAddress: "0xtoken", + PaymentVaultAddress: "0xvault", }, nil } @@ -222,6 +228,12 @@ func TestGrpcHealth(t *testing.T) { if !h.OK || h.Network != "local" { t.Fatalf("unexpected health: %+v", h) } + if h.Version != "0.4.0" || h.EvmNetwork != "local" || h.UptimeSeconds != 42 { + t.Fatalf("unexpected diagnostic fields: %+v", h) + } + if h.BuildCommit != "abcdef123456" || h.PaymentTokenAddress != "0xtoken" || h.PaymentVaultAddress != "0xvault" { + t.Fatalf("unexpected build/payment fields: %+v", h) + } } func TestGrpcDataPutPublic(t *testing.T) { diff --git a/antd-go/models.go b/antd-go/models.go index 1120f53..deeddc6 100644 --- a/antd-go/models.go +++ b/antd-go/models.go @@ -2,8 +2,14 @@ package antd // HealthStatus is the result of a health check. type HealthStatus struct { - OK bool `json:"ok"` - Network string `json:"network"` + OK bool `json:"ok"` + Network string `json:"network"` + Version string `json:"version"` // antd crate version + EvmNetwork string `json:"evm_network"` // arbitrum-one, arbitrum-sepolia, local, custom + UptimeSeconds uint64 `json:"uptime_seconds"` // seconds since daemon start + BuildCommit string `json:"build_commit"` // short git SHA, "" if unknown + PaymentTokenAddress string `json:"payment_token_address"` + PaymentVaultAddress string `json:"payment_vault_address"` } // PutResult is the result of a put/create operation. diff --git a/antd-go/proto/antd/v1/chunks.pb.go b/antd-go/proto/antd/v1/chunks.pb.go index a034ce6..fd28b76 100644 --- a/antd-go/proto/antd/v1/chunks.pb.go +++ b/antd-go/proto/antd/v1/chunks.pb.go @@ -221,8 +221,7 @@ const file_antd_v1_chunks_proto_rawDesc = "" + "\aaddress\x18\x02 \x01(\tR\aaddress2\x86\x01\n" + "\fChunkService\x12:\n" + "\x03Get\x12\x18.antd.v1.GetChunkRequest\x1a\x19.antd.v1.GetChunkResponse\x12:\n" + - "\x03Put\x12\x18.antd.v1.PutChunkRequest\x1a\x19.antd.v1.PutChunkResponseB\n" + - "\xaa\x02\aAntd.V1b\x06proto3" + "\x03Put\x12\x18.antd.v1.PutChunkRequest\x1a\x19.antd.v1.PutChunkResponseBDZ8github.com/WithAutonomi/ant-sdk/antd-go/proto/antd/v1;v1\xaa\x02\aAntd.V1b\x06proto3" var ( file_antd_v1_chunks_proto_rawDescOnce sync.Once diff --git a/antd-go/proto/antd/v1/common.pb.go b/antd-go/proto/antd/v1/common.pb.go index 189c50b..4939f69 100644 --- a/antd-go/proto/antd/v1/common.pb.go +++ b/antd-go/proto/antd/v1/common.pb.go @@ -22,15 +22,12 @@ const ( ) type Cost struct { - state protoimpl.MessageState `protogen:"open.v1"` - AttoTokens string `protobuf:"bytes,1,opt,name=atto_tokens,json=attoTokens,proto3" json:"atto_tokens,omitempty"` // AttoTokens as string (can exceed JS Number.MAX_SAFE_INTEGER) - // Fields populated only by cost-estimation RPCs (GetCost, GetFileCost). - // Left at zero/empty for cost values returned by put/upload RPCs, where - // payment already happened and these fields are not meaningful. - FileSize uint64 `protobuf:"varint,2,opt,name=file_size,json=fileSize,proto3" json:"file_size,omitempty"` // original file size in bytes - ChunkCount uint32 `protobuf:"varint,3,opt,name=chunk_count,json=chunkCount,proto3" json:"chunk_count,omitempty"` // number of data chunks the file splits into - EstimatedGasCostWei string `protobuf:"bytes,4,opt,name=estimated_gas_cost_wei,json=estimatedGasCostWei,proto3" json:"estimated_gas_cost_wei,omitempty"` // gas heuristic as string (can exceed JS safe int) - PaymentMode string `protobuf:"bytes,5,opt,name=payment_mode,json=paymentMode,proto3" json:"payment_mode,omitempty"` // "auto" | "merkle" | "single" + state protoimpl.MessageState `protogen:"open.v1"` + AttoTokens string `protobuf:"bytes,1,opt,name=atto_tokens,json=attoTokens,proto3" json:"atto_tokens,omitempty"` // storage cost in atto tokens as string + FileSize uint64 `protobuf:"varint,2,opt,name=file_size,json=fileSize,proto3" json:"file_size,omitempty"` // original file size in bytes + ChunkCount uint32 `protobuf:"varint,3,opt,name=chunk_count,json=chunkCount,proto3" json:"chunk_count,omitempty"` // number of data chunks the file splits into + EstimatedGasCostWei string `protobuf:"bytes,4,opt,name=estimated_gas_cost_wei,json=estimatedGasCostWei,proto3" json:"estimated_gas_cost_wei,omitempty"` // gas heuristic as string (can exceed JS safe int) + PaymentMode string `protobuf:"bytes,5,opt,name=payment_mode,json=paymentMode,proto3" json:"payment_mode,omitempty"` // "auto" | "merkle" | "single" unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -306,8 +303,7 @@ const file_antd_v1_common_proto_rawDesc = "" + "\x0fGraphDescendant\x12\x1d\n" + "\n" + "public_key\x18\x01 \x01(\tR\tpublicKey\x12\x18\n" + - "\acontent\x18\x02 \x01(\tR\acontentB\n" + - "\xaa\x02\aAntd.V1b\x06proto3" + "\acontent\x18\x02 \x01(\tR\acontentBDZ8github.com/WithAutonomi/ant-sdk/antd-go/proto/antd/v1;v1\xaa\x02\aAntd.V1b\x06proto3" var ( file_antd_v1_common_proto_rawDescOnce sync.Once diff --git a/antd-go/proto/antd/v1/data.pb.go b/antd-go/proto/antd/v1/data.pb.go index 8ef262e..c29b1ab 100644 --- a/antd-go/proto/antd/v1/data.pb.go +++ b/antd-go/proto/antd/v1/data.pb.go @@ -558,8 +558,7 @@ const file_antd_v1_data_proto_rawDesc = "" + "GetPrivate\x12\x1e.antd.v1.GetPrivateDataRequest\x1a\x1f.antd.v1.GetPrivateDataResponse\x12M\n" + "\n" + "PutPrivate\x12\x1e.antd.v1.PutPrivateDataRequest\x1a\x1f.antd.v1.PutPrivateDataResponse\x122\n" + - "\aGetCost\x12\x18.antd.v1.DataCostRequest\x1a\r.antd.v1.CostB\n" + - "\xaa\x02\aAntd.V1b\x06proto3" + "\aGetCost\x12\x18.antd.v1.DataCostRequest\x1a\r.antd.v1.CostBDZ8github.com/WithAutonomi/ant-sdk/antd-go/proto/antd/v1;v1\xaa\x02\aAntd.V1b\x06proto3" var ( file_antd_v1_data_proto_rawDescOnce sync.Once diff --git a/antd-go/proto/antd/v1/events.pb.go b/antd-go/proto/antd/v1/events.pb.go index 8353ce0..a863d51 100644 --- a/antd-go/proto/antd/v1/events.pb.go +++ b/antd-go/proto/antd/v1/events.pb.go @@ -137,8 +137,7 @@ const file_antd_v1_events_proto_rawDesc = "" + "\x14records_already_paid\x18\x03 \x01(\x04R\x12recordsAlreadyPaid\x12!\n" + "\ftokens_spent\x18\x04 \x01(\tR\vtokensSpent2S\n" + "\fEventService\x12C\n" + - "\tSubscribe\x12\x19.antd.v1.SubscribeRequest\x1a\x19.antd.v1.ClientEventProto0\x01B\n" + - "\xaa\x02\aAntd.V1b\x06proto3" + "\tSubscribe\x12\x19.antd.v1.SubscribeRequest\x1a\x19.antd.v1.ClientEventProto0\x01BDZ8github.com/WithAutonomi/ant-sdk/antd-go/proto/antd/v1;v1\xaa\x02\aAntd.V1b\x06proto3" var ( file_antd_v1_events_proto_rawDescOnce sync.Once diff --git a/antd-go/proto/antd/v1/files.pb.go b/antd-go/proto/antd/v1/files.pb.go index 930e63b..202059e 100644 --- a/antd-go/proto/antd/v1/files.pb.go +++ b/antd-go/proto/antd/v1/files.pb.go @@ -311,8 +311,7 @@ const file_antd_v1_files_proto_rawDesc = "" + "\x0eDownloadPublic\x12\x1e.antd.v1.DownloadPublicRequest\x1a\x19.antd.v1.DownloadResponse\x12L\n" + "\x0fDirUploadPublic\x12\x1a.antd.v1.UploadFileRequest\x1a\x1d.antd.v1.UploadPublicResponse\x12N\n" + "\x11DirDownloadPublic\x12\x1e.antd.v1.DownloadPublicRequest\x1a\x19.antd.v1.DownloadResponse\x126\n" + - "\vGetFileCost\x12\x18.antd.v1.FileCostRequest\x1a\r.antd.v1.CostB\n" + - "\xaa\x02\aAntd.V1b\x06proto3" + "\vGetFileCost\x12\x18.antd.v1.FileCostRequest\x1a\r.antd.v1.CostBDZ8github.com/WithAutonomi/ant-sdk/antd-go/proto/antd/v1;v1\xaa\x02\aAntd.V1b\x06proto3" var ( file_antd_v1_files_proto_rawDescOnce sync.Once diff --git a/antd-go/proto/antd/v1/health.pb.go b/antd-go/proto/antd/v1/health.pb.go index c63736f..62b6e75 100644 --- a/antd-go/proto/antd/v1/health.pb.go +++ b/antd-go/proto/antd/v1/health.pb.go @@ -58,11 +58,17 @@ func (*HealthCheckRequest) Descriptor() ([]byte, []int) { } type HealthCheckResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - Status string `protobuf:"bytes,1,opt,name=status,proto3" json:"status,omitempty"` // "ok" - Network string `protobuf:"bytes,2,opt,name=network,proto3" json:"network,omitempty"` // "default", "local", "alpha" - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + Status string `protobuf:"bytes,1,opt,name=status,proto3" json:"status,omitempty"` // "ok" + Network string `protobuf:"bytes,2,opt,name=network,proto3" json:"network,omitempty"` // "default", "local", "alpha" + Version string `protobuf:"bytes,3,opt,name=version,proto3" json:"version,omitempty"` // antd crate version (e.g. "0.4.0") + EvmNetwork string `protobuf:"bytes,4,opt,name=evm_network,json=evmNetwork,proto3" json:"evm_network,omitempty"` // EVM preset: "arbitrum-one", "arbitrum-sepolia", "local", "custom" + UptimeSeconds uint64 `protobuf:"varint,5,opt,name=uptime_seconds,json=uptimeSeconds,proto3" json:"uptime_seconds,omitempty"` // seconds since process start + BuildCommit string `protobuf:"bytes,6,opt,name=build_commit,json=buildCommit,proto3" json:"build_commit,omitempty"` // short git SHA, or "" if built outside a git checkout + PaymentTokenAddress string `protobuf:"bytes,7,opt,name=payment_token_address,json=paymentTokenAddress,proto3" json:"payment_token_address,omitempty"` // token contract, or "" if unconfigured + PaymentVaultAddress string `protobuf:"bytes,8,opt,name=payment_vault_address,json=paymentVaultAddress,proto3" json:"payment_vault_address,omitempty"` // payment vault contract, or "" if unconfigured + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *HealthCheckResponse) Reset() { @@ -109,18 +115,66 @@ func (x *HealthCheckResponse) GetNetwork() string { return "" } +func (x *HealthCheckResponse) GetVersion() string { + if x != nil { + return x.Version + } + return "" +} + +func (x *HealthCheckResponse) GetEvmNetwork() string { + if x != nil { + return x.EvmNetwork + } + return "" +} + +func (x *HealthCheckResponse) GetUptimeSeconds() uint64 { + if x != nil { + return x.UptimeSeconds + } + return 0 +} + +func (x *HealthCheckResponse) GetBuildCommit() string { + if x != nil { + return x.BuildCommit + } + return "" +} + +func (x *HealthCheckResponse) GetPaymentTokenAddress() string { + if x != nil { + return x.PaymentTokenAddress + } + return "" +} + +func (x *HealthCheckResponse) GetPaymentVaultAddress() string { + if x != nil { + return x.PaymentVaultAddress + } + return "" +} + var File_antd_v1_health_proto protoreflect.FileDescriptor const file_antd_v1_health_proto_rawDesc = "" + "\n" + "\x14antd/v1/health.proto\x12\aantd.v1\"\x14\n" + - "\x12HealthCheckRequest\"G\n" + + "\x12HealthCheckRequest\"\xb4\x02\n" + "\x13HealthCheckResponse\x12\x16\n" + "\x06status\x18\x01 \x01(\tR\x06status\x12\x18\n" + - "\anetwork\x18\x02 \x01(\tR\anetwork2S\n" + + "\anetwork\x18\x02 \x01(\tR\anetwork\x12\x18\n" + + "\aversion\x18\x03 \x01(\tR\aversion\x12\x1f\n" + + "\vevm_network\x18\x04 \x01(\tR\n" + + "evmNetwork\x12%\n" + + "\x0euptime_seconds\x18\x05 \x01(\x04R\ruptimeSeconds\x12!\n" + + "\fbuild_commit\x18\x06 \x01(\tR\vbuildCommit\x122\n" + + "\x15payment_token_address\x18\a \x01(\tR\x13paymentTokenAddress\x122\n" + + "\x15payment_vault_address\x18\b \x01(\tR\x13paymentVaultAddress2S\n" + "\rHealthService\x12B\n" + - "\x05Check\x12\x1b.antd.v1.HealthCheckRequest\x1a\x1c.antd.v1.HealthCheckResponseB\n" + - "\xaa\x02\aAntd.V1b\x06proto3" + "\x05Check\x12\x1b.antd.v1.HealthCheckRequest\x1a\x1c.antd.v1.HealthCheckResponseBDZ8github.com/WithAutonomi/ant-sdk/antd-go/proto/antd/v1;v1\xaa\x02\aAntd.V1b\x06proto3" var ( file_antd_v1_health_proto_rawDescOnce sync.Once diff --git a/antd-rust/src/grpc_tests.rs b/antd-rust/src/grpc_tests.rs index 888ed66..1ed5892 100644 --- a/antd-rust/src/grpc_tests.rs +++ b/antd-rust/src/grpc_tests.rs @@ -20,6 +20,11 @@ impl v1::health_service_server::HealthService for MockHealthService { Ok(Response::new(v1::HealthCheckResponse { status: "ok".to_string(), network: "local".to_string(), + // Diagnostic fields added in 0.4.0 — left as defaults here + // because antd-rust's HealthStatus model has not yet been + // extended (tracked in issue #37). The mock just needs to + // satisfy the wire type. + ..Default::default() })) } } diff --git a/antd/Cargo.lock b/antd/Cargo.lock index d95c955..f381c84 100644 --- a/antd/Cargo.lock +++ b/antd/Cargo.lock @@ -932,7 +932,7 @@ dependencies = [ [[package]] name = "antd" -version = "0.3.0" +version = "0.4.0" dependencies = [ "ant-core", "axum 0.8.9", diff --git a/antd/Cargo.toml b/antd/Cargo.toml index 8b78404..404ea23 100644 --- a/antd/Cargo.toml +++ b/antd/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "antd" -version = "0.3.0" +version = "0.4.0" edition = "2021" [dependencies] diff --git a/antd/build.rs b/antd/build.rs index b19c967..4a12b27 100644 --- a/antd/build.rs +++ b/antd/build.rs @@ -1,4 +1,20 @@ +use std::process::Command; + fn main() -> Result<(), Box> { + // Capture short git SHA for /health diagnostics. Falls back to "" when + // built outside a git checkout (e.g. crates.io source distribution) — the + // /health endpoint reports the empty string in that case. + let commit = Command::new("git") + .args(["rev-parse", "--short=12", "HEAD"]) + .output() + .ok() + .filter(|o| o.status.success()) + .map(|o| String::from_utf8_lossy(&o.stdout).trim().to_string()) + .unwrap_or_default(); + println!("cargo:rustc-env=ANTD_BUILD_COMMIT={commit}"); + println!("cargo:rerun-if-changed=.git/HEAD"); + println!("cargo:rerun-if-changed=.git/refs/heads"); + tonic_build::configure() .build_client(false) .build_server(true) diff --git a/antd/openapi.yaml b/antd/openapi.yaml index 36c90d9..f7526c8 100644 --- a/antd/openapi.yaml +++ b/antd/openapi.yaml @@ -519,7 +519,15 @@ components: # -- Health -- HealthResponse: type: object - required: [status, network] + required: + - status + - network + - version + - evm_network + - uptime_seconds + - build_commit + - payment_token_address + - payment_vault_address properties: status: type: string @@ -528,6 +536,32 @@ components: type: string example: "local" description: "Network name: default, local, or alpha" + version: + type: string + example: "0.4.0" + description: "antd crate version (CARGO_PKG_VERSION)" + evm_network: + type: string + example: "arbitrum-one" + description: "EVM preset: arbitrum-one, arbitrum-sepolia, local, or custom" + uptime_seconds: + type: integer + format: int64 + minimum: 0 + example: 12345 + description: "Seconds since the daemon process started" + build_commit: + type: string + example: "1cbfb3ec85ad" + description: "Short git SHA captured at build time, or empty if unknown" + payment_token_address: + type: string + example: "0xde817De9d8AC8C3aA10C3Ed0EE5FCB6C53cE7B0a" + description: "Payment token contract address, or empty if unconfigured" + payment_vault_address: + type: string + example: "0x607483B50C5F06c25cDC316b6d1E071084EeC9f5" + description: "Payment vault contract address, or empty if unconfigured" # -- Data -- DataPutRequest: diff --git a/antd/proto/antd/v1/chunks.proto b/antd/proto/antd/v1/chunks.proto index 66443f7..09bb9b4 100644 --- a/antd/proto/antd/v1/chunks.proto +++ b/antd/proto/antd/v1/chunks.proto @@ -3,6 +3,7 @@ syntax = "proto3"; package antd.v1; option csharp_namespace = "Antd.V1"; +option go_package = "github.com/WithAutonomi/ant-sdk/antd-go/proto/antd/v1;v1"; import "antd/v1/common.proto"; diff --git a/antd/proto/antd/v1/common.proto b/antd/proto/antd/v1/common.proto index 9646083..f4f44b8 100644 --- a/antd/proto/antd/v1/common.proto +++ b/antd/proto/antd/v1/common.proto @@ -3,6 +3,7 @@ syntax = "proto3"; package antd.v1; option csharp_namespace = "Antd.V1"; +option go_package = "github.com/WithAutonomi/ant-sdk/antd-go/proto/antd/v1;v1"; message Cost { string atto_tokens = 1; // storage cost in atto tokens as string diff --git a/antd/proto/antd/v1/data.proto b/antd/proto/antd/v1/data.proto index 2b1ea69..79f041c 100644 --- a/antd/proto/antd/v1/data.proto +++ b/antd/proto/antd/v1/data.proto @@ -3,6 +3,7 @@ syntax = "proto3"; package antd.v1; option csharp_namespace = "Antd.V1"; +option go_package = "github.com/WithAutonomi/ant-sdk/antd-go/proto/antd/v1;v1"; import "antd/v1/common.proto"; diff --git a/antd/proto/antd/v1/events.proto b/antd/proto/antd/v1/events.proto index 5781714..cf498e3 100644 --- a/antd/proto/antd/v1/events.proto +++ b/antd/proto/antd/v1/events.proto @@ -3,6 +3,7 @@ syntax = "proto3"; package antd.v1; option csharp_namespace = "Antd.V1"; +option go_package = "github.com/WithAutonomi/ant-sdk/antd-go/proto/antd/v1;v1"; service EventService { rpc Subscribe(SubscribeRequest) returns (stream ClientEventProto); diff --git a/antd/proto/antd/v1/files.proto b/antd/proto/antd/v1/files.proto index 57d4958..6ad6a25 100644 --- a/antd/proto/antd/v1/files.proto +++ b/antd/proto/antd/v1/files.proto @@ -3,6 +3,7 @@ syntax = "proto3"; package antd.v1; option csharp_namespace = "Antd.V1"; +option go_package = "github.com/WithAutonomi/ant-sdk/antd-go/proto/antd/v1;v1"; import "antd/v1/common.proto"; diff --git a/antd/proto/antd/v1/health.proto b/antd/proto/antd/v1/health.proto index b98c774..35cea5a 100644 --- a/antd/proto/antd/v1/health.proto +++ b/antd/proto/antd/v1/health.proto @@ -3,6 +3,7 @@ syntax = "proto3"; package antd.v1; option csharp_namespace = "Antd.V1"; +option go_package = "github.com/WithAutonomi/ant-sdk/antd-go/proto/antd/v1;v1"; service HealthService { rpc Check(HealthCheckRequest) returns (HealthCheckResponse); @@ -13,4 +14,10 @@ message HealthCheckRequest {} message HealthCheckResponse { string status = 1; // "ok" string network = 2; // "default", "local", "alpha" + string version = 3; // antd crate version (e.g. "0.4.0") + string evm_network = 4; // EVM preset: "arbitrum-one", "arbitrum-sepolia", "local", "custom" + uint64 uptime_seconds = 5; // seconds since process start + string build_commit = 6; // short git SHA, or "" if built outside a git checkout + string payment_token_address = 7; // token contract, or "" if unconfigured + string payment_vault_address = 8; // payment vault contract, or "" if unconfigured } diff --git a/antd/src/grpc/mod.rs b/antd/src/grpc/mod.rs index efc9898..a2a772c 100644 --- a/antd/src/grpc/mod.rs +++ b/antd/src/grpc/mod.rs @@ -31,7 +31,7 @@ pub async fn serve( state: state.clone(), }); let health_svc = HealthServiceServer::new(service::HealthServiceImpl { - network: state.network.clone(), + state: state.clone(), }); let addr = listener.local_addr()?; diff --git a/antd/src/grpc/service.rs b/antd/src/grpc/service.rs index 2d1dc63..0e72ae0 100644 --- a/antd/src/grpc/service.rs +++ b/antd/src/grpc/service.rs @@ -21,7 +21,7 @@ fn not_implemented(op: &str) -> Status { // ── HealthService ── pub struct HealthServiceImpl { - pub network: String, + pub state: Arc, } #[tonic::async_trait] @@ -32,7 +32,13 @@ impl pb::health_service_server::HealthService for HealthServiceImpl { ) -> Result, Status> { Ok(Response::new(pb::HealthCheckResponse { status: "ok".into(), - network: self.network.clone(), + network: self.state.network.clone(), + version: self.state.version.clone(), + evm_network: self.state.evm_preset.clone(), + uptime_seconds: self.state.started_at.elapsed().as_secs(), + build_commit: self.state.build_commit.clone(), + payment_token_address: self.state.evm_token_addr.clone(), + payment_vault_address: self.state.evm_vault_addr.clone(), })) } } diff --git a/antd/src/main.rs b/antd/src/main.rs index df5b788..3808758 100644 --- a/antd/src/main.rs +++ b/antd/src/main.rs @@ -211,6 +211,12 @@ async fn main() -> Result<(), Box> { "EVM config resolved" ); + // Capture preset + addresses for /health diagnostics before evm_cfg is + // consumed by the wallet/network setup below. + let evm_preset = evm_cfg.preset.clone(); + let evm_token_addr = evm_cfg.token_addr.clone(); + let evm_vault_addr = evm_cfg.vault_addr.clone(); + // For known mainnet/testnet presets use the typed EvmNetwork variants // (ArbitrumOne / ArbitrumSepoliaTest) — they encode the chain-id and // pricing constants that mainnet storers' median-quote payment verifier @@ -269,6 +275,12 @@ async fn main() -> Result<(), Box> { network: config.network.clone(), bootstrap_peers, pending_uploads: Arc::new(tokio::sync::Mutex::new(std::collections::HashMap::new())), + started_at: std::time::Instant::now(), + version: env!("CARGO_PKG_VERSION").to_string(), + build_commit: env!("ANTD_BUILD_COMMIT").to_string(), + evm_preset, + evm_token_addr, + evm_vault_addr, }); // Spawn background task to clean up stale pending uploads (1-hour TTL) diff --git a/antd/src/rest/mod.rs b/antd/src/rest/mod.rs index 49ce48a..3f111c5 100644 --- a/antd/src/rest/mod.rs +++ b/antd/src/rest/mod.rs @@ -123,5 +123,11 @@ async fn health(State(state): State>) -> Json { Json(HealthResponse { status: "ok".into(), network: state.network.clone(), + version: state.version.clone(), + evm_network: state.evm_preset.clone(), + uptime_seconds: state.started_at.elapsed().as_secs(), + build_commit: state.build_commit.clone(), + payment_token_address: state.evm_token_addr.clone(), + payment_vault_address: state.evm_vault_addr.clone(), }) } diff --git a/antd/src/state.rs b/antd/src/state.rs index 92bd4c2..f969280 100644 --- a/antd/src/state.rs +++ b/antd/src/state.rs @@ -22,6 +22,18 @@ pub struct AppState { pub bootstrap_peers: Vec, /// Pending prepared uploads awaiting external payment (upload_id → state). pub pending_uploads: Arc>>, + /// Process start time, for /health uptime reporting. + pub started_at: std::time::Instant, + /// antd crate version (env!("CARGO_PKG_VERSION") at build time). + pub version: String, + /// Short git SHA captured by build.rs, or "" if unknown. + pub build_commit: String, + /// EVM preset name ("arbitrum-one", "arbitrum-sepolia", "local", "custom"). + pub evm_preset: String, + /// Payment token contract address, or "" if unconfigured. + pub evm_token_addr: String, + /// Payment vault contract address, or "" if unconfigured. + pub evm_vault_addr: String, } impl AppState { diff --git a/antd/src/types.rs b/antd/src/types.rs index 88e83b6..e363bc8 100644 --- a/antd/src/types.rs +++ b/antd/src/types.rs @@ -283,6 +283,12 @@ pub struct WalletApproveResponse { pub struct HealthResponse { pub status: String, pub network: String, + pub version: String, + pub evm_network: String, + pub uptime_seconds: u64, + pub build_commit: String, + pub payment_token_address: String, + pub payment_vault_address: String, } // ── Tests ── @@ -401,4 +407,48 @@ mod tests { assert!(req.tx_hashes.is_some()); assert!(req.tx_hashes.as_ref().unwrap().is_empty()); } + + #[test] + fn health_response_serializes_all_fields() { + let resp = HealthResponse { + status: "ok".into(), + network: "default".into(), + version: "0.4.0".into(), + evm_network: "arbitrum-one".into(), + uptime_seconds: 12345, + build_commit: "abcdef123456".into(), + payment_token_address: "0xtoken".into(), + payment_vault_address: "0xvault".into(), + }; + let json = serde_json::to_value(&resp).unwrap(); + assert_eq!(json["status"], "ok"); + assert_eq!(json["network"], "default"); + assert_eq!(json["version"], "0.4.0"); + assert_eq!(json["evm_network"], "arbitrum-one"); + assert_eq!(json["uptime_seconds"], 12345u64); + assert_eq!(json["build_commit"], "abcdef123456"); + assert_eq!(json["payment_token_address"], "0xtoken"); + assert_eq!(json["payment_vault_address"], "0xvault"); + } + + #[test] + fn health_response_keeps_empty_strings_for_unconfigured_evm() { + // Local devnet leaves token + vault empty; build_commit is empty for + // non-git source distributions. Empty strings must round-trip rather + // than being omitted. + let resp = HealthResponse { + status: "ok".into(), + network: "local".into(), + version: "0.4.0".into(), + evm_network: "local".into(), + uptime_seconds: 0, + build_commit: String::new(), + payment_token_address: String::new(), + payment_vault_address: String::new(), + }; + let json = serde_json::to_value(&resp).unwrap(); + assert_eq!(json["build_commit"], ""); + assert_eq!(json["payment_token_address"], ""); + assert_eq!(json["payment_vault_address"], ""); + } }