From 04fcf75ae5050bfb9ea0b1f944b6218f9ca8a735 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 20 Feb 2026 21:11:44 +0000 Subject: [PATCH] feat(rpc): SubmitBlock endpoint Co-Authored-By: Charon Co-Authored-By: Claude Opus 4.6 --- rpc/mining.go | 26 +++++++++++++++++ rpc/mining_test.go | 69 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+) create mode 100644 rpc/mining.go create mode 100644 rpc/mining_test.go diff --git a/rpc/mining.go b/rpc/mining.go new file mode 100644 index 0000000..994ec7d --- /dev/null +++ b/rpc/mining.go @@ -0,0 +1,26 @@ +// Copyright (c) 2017-2026 Lethean (https://lt.hn) +// +// Licensed under the European Union Public Licence (EUPL) version 1.2. +// SPDX-License-Identifier: EUPL-1.2 + +package rpc + +import "fmt" + +// SubmitBlock submits a mined block to the daemon. +// The hexBlob is the hex-encoded serialised block. +// Note: submitblock takes a JSON array as params, not an object. +func (c *Client) SubmitBlock(hexBlob string) error { + // submitblock expects params as an array: ["hexblob"] + params := []string{hexBlob} + var resp struct { + Status string `json:"status"` + } + if err := c.call("submitblock", params, &resp); err != nil { + return err + } + if resp.Status != "OK" { + return fmt.Errorf("submitblock: status %q", resp.Status) + } + return nil +} diff --git a/rpc/mining_test.go b/rpc/mining_test.go new file mode 100644 index 0000000..491b8d8 --- /dev/null +++ b/rpc/mining_test.go @@ -0,0 +1,69 @@ +// Copyright (c) 2017-2026 Lethean (https://lt.hn) +// +// Licensed under the European Union Public Licence (EUPL) version 1.2. +// SPDX-License-Identifier: EUPL-1.2 + +package rpc + +import ( + "encoding/json" + "io" + "net/http" + "net/http/httptest" + "testing" +) + +func TestSubmitBlock_Good(t *testing.T) { + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + body, _ := io.ReadAll(r.Body) + var req jsonRPCRequest + json.Unmarshal(body, &req) + if req.Method != "submitblock" { + t.Errorf("method: got %q, want %q", req.Method, "submitblock") + } + // Verify params is an array. + raw, _ := json.Marshal(req.Params) + if raw[0] != '[' { + t.Errorf("params should be array, got: %s", raw) + } + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(jsonRPCResponse{ + JSONRPC: "2.0", + ID: json.RawMessage(`"0"`), + Result: json.RawMessage(`{"status":"OK"}`), + }) + })) + defer srv.Close() + + c := NewClient(srv.URL) + err := c.SubmitBlock("0300000001020304") + if err != nil { + t.Fatalf("SubmitBlock: %v", err) + } +} + +func TestSubmitBlock_Bad_Rejected(t *testing.T) { + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(jsonRPCResponse{ + JSONRPC: "2.0", + ID: json.RawMessage(`"0"`), + Error: &jsonRPCError{Code: -7, Message: "BLOCK_NOT_ACCEPTED"}, + }) + })) + defer srv.Close() + + c := NewClient(srv.URL) + err := c.SubmitBlock("invalid") + if err == nil { + t.Fatal("expected error") + } + rpcErr, ok := err.(*RPCError) + if !ok { + t.Fatalf("expected *RPCError, got %T", err) + } + if rpcErr.Code != -7 { + t.Errorf("code: got %d, want -7", rpcErr.Code) + } +}