diff --git a/app/grpc_server/main.go b/app/grpc_server/main.go index c7149b1..69e881d 100644 --- a/app/grpc_server/main.go +++ b/app/grpc_server/main.go @@ -31,9 +31,10 @@ func run(log *log.Logger) error { ShutdownTimeout time.Duration `conf:"default:5s"` HttpHost string `conf:"default:0.0.0.0:8000"` GrpcHost string `conf:"default:0.0.0.0:8001"` + MaxTickFetchUrl string `conf:"default:http://127.0.0.1:8080/max-tick"` } Pool struct { - NodeFetcherUrl string `conf:"default:http://127.0.0.1:8080/peers"` + NodeFetcherUrl string `conf:"default:http://127.0.0.1:8080/status"` NodeFetcherTimeout time.Duration `conf:"default:2s"` InitialCap int `conf:"default:5"` MaxIdle int `conf:"default:20"` @@ -81,7 +82,7 @@ func run(log *log.Logger) error { return errors.Wrap(err, "creating qubic pool") } - rpcServer := rpc.NewServer(cfg.Server.GrpcHost, cfg.Server.HttpHost, pool) + rpcServer := rpc.NewServer(cfg.Server.GrpcHost, cfg.Server.HttpHost, pool, cfg.Server.MaxTickFetchUrl) rpcServer.Start() shutdown := make(chan os.Signal, 1) diff --git a/foundation/rpc_server/rpc_server.go b/foundation/rpc_server/rpc_server.go index 9cd6750..94d05e4 100644 --- a/foundation/rpc_server/rpc_server.go +++ b/foundation/rpc_server/rpc_server.go @@ -1,12 +1,18 @@ package rpc import ( + "bytes" "context" "encoding/base64" + "encoding/json" "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" + "github.com/pkg/errors" + "github.com/qubic/go-node-connector/types" + "github.com/qubic/go-schnorrq" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "google.golang.org/protobuf/types/known/emptypb" + "io" qubic "github.com/qubic/go-node-connector" "github.com/qubic/qubic-http/protobuff" @@ -26,16 +32,18 @@ var _ protobuff.QubicLiveServiceServer = &Server{} type Server struct { protobuff.UnimplementedQubicLiveServiceServer - listenAddrGRPC string - listenAddrHTTP string - qPool *qubic.Pool + listenAddrGRPC string + listenAddrHTTP string + qPool *qubic.Pool + maxTickFetchUrl string } -func NewServer(listenAddrGRPC, listenAddrHTTP string, qPool *qubic.Pool) *Server { +func NewServer(listenAddrGRPC, listenAddrHTTP string, qPool *qubic.Pool, maxTickFetchUrl string) *Server { return &Server{ - listenAddrGRPC: listenAddrGRPC, - listenAddrHTTP: listenAddrHTTP, - qPool: qPool, + listenAddrGRPC: listenAddrGRPC, + listenAddrHTTP: listenAddrHTTP, + qPool: qPool, + maxTickFetchUrl: maxTickFetchUrl, } } @@ -105,11 +113,70 @@ func (s *Server) GetBlockHeight(ctx context.Context, _ *emptypb.Empty) (*protobu }}, nil } +type maxTickResponse struct { + MaxTick uint32 `json:"max_tick"` +} + +func fetchMaxTick(ctx context.Context, maxTickFetchUrl string) (uint32, error) { + req, err := http.NewRequestWithContext(ctx, http.MethodGet, maxTickFetchUrl, nil) + if err != nil { + return 0, errors.Wrap(err, "creating new request") + } + + res, err := http.DefaultClient.Do(req) + if err != nil { + return 0, errors.Wrap(err, "performing request") + } + + var resp maxTickResponse + body, err := io.ReadAll(res.Body) + if err != nil { + return 0, errors.Wrap(err, "reading response body") + } + err = json.Unmarshal(body, &resp) + if err != nil { + return 0, errors.Wrap(err, "unmarshalling response") + } + + tick := resp.MaxTick + + return tick, nil +} + func (s *Server) BroadcastTransaction(ctx context.Context, req *protobuff.BroadcastTransactionRequest) (*protobuff.BroadcastTransactionResponse, error) { decodedTx, err := base64.StdEncoding.DecodeString(req.EncodedTransaction) if err != nil { return nil, status.Error(codes.InvalidArgument, err.Error()) } + + reader := bytes.NewReader(decodedTx) + + var transaction types.Transaction + err = transaction.UnmarshallBinary(reader) + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + + digest, err := transaction.GetUnsignedDigest() + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + + err = schnorrq.Verify(transaction.SourcePublicKey, digest, transaction.Signature) + if err != nil { + return nil, status.Error(codes.InvalidArgument, err.Error()) + } + + maxTick, err := fetchMaxTick(ctx, s.maxTickFetchUrl) + + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + + if transaction.Tick < maxTick { + return nil, status.Error(codes.InvalidArgument, "Transaction cannot be on an already processed tick.") + } + return &protobuff.BroadcastTransactionResponse{PeersBroadcasted: int32(broadcastTxToMultiple(ctx, s.qPool, decodedTx)), EncodedTransaction: req.EncodedTransaction}, nil } diff --git a/go.mod b/go.mod index ad2b27b..663e444 100644 --- a/go.mod +++ b/go.mod @@ -1,22 +1,28 @@ module github.com/qubic/qubic-http -go 1.21.3 +go 1.22 + +toolchain go1.22.2 require ( github.com/ardanlabs/conf v1.5.0 github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1 github.com/pkg/errors v0.9.1 - github.com/qubic/go-node-connector v0.6.1 + github.com/qubic/go-node-connector v0.6.2 + github.com/qubic/go-schnorrq v1.0.0 google.golang.org/genproto/googleapis/api v0.0.0-20240314234333-6e1732d8331c google.golang.org/grpc v1.62.1 google.golang.org/protobuf v1.33.0 ) require ( + github.com/bits-and-blooms/bitset v1.13.0 // indirect github.com/cloudflare/circl v1.3.6 // indirect + github.com/consensys/gnark-crypto v0.12.1 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect + github.com/linckode/circl v1.3.71 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/silenceper/pool v1.0.0 // indirect github.com/sirupsen/logrus v1.4.2 // indirect diff --git a/go.sum b/go.sum index d721771..51c0666 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,13 @@ github.com/ardanlabs/conf v1.5.0 h1:5TwP6Wu9Xi07eLFEpiCUF3oQXh9UzHMDVnD3u/I5d5c= github.com/ardanlabs/conf v1.5.0/go.mod h1:ILsMo9dMqYzCxDjDXTiwMI0IgxOJd0MOiucbQY2wlJw= +github.com/bits-and-blooms/bitset v1.13.0 h1:bAQ9OPNFYbGHV6Nez0tmNI0RiEu7/hxlYJRUA0wFAVE= +github.com/bits-and-blooms/bitset v1.13.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/cloudflare/circl v1.3.6 h1:/xbKIqSHbZXHwkhbrhrt2YOHIwYJlXH94E3tI/gDlUg= github.com/cloudflare/circl v1.3.6/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= github.com/cloudflare/fourq v0.0.0-20170427000316-8ada258cf9c8 h1:748sGeXXbplK0UVPDLbhh53hejCnvv/u6jn2RPBfyI8= github.com/cloudflare/fourq v0.0.0-20170427000316-8ada258cf9c8/go.mod h1:13nQglQo5cpucnNY80duyW/6HK+WQ9+dHZ70UzAy6Jw= +github.com/consensys/gnark-crypto v0.12.1 h1:lHH39WuuFgVHONRl3J0LRBtuYdQTumFSDtJF7HpyG8M= +github.com/consensys/gnark-crypto v0.12.1/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5UlG+EM5t7MPHiLuY= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -19,15 +23,19 @@ github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1/go.mod h1:5SN9VR2LTsRFsrEC6FHg github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/leanovate/gopter v0.2.11 h1:vRjThO1EKPb/1NsDXuDrzldR28RLkBflWYcU9CvzWu4= +github.com/leanovate/gopter v0.2.11/go.mod h1:aK3tzZP/C+p1m3SPRE4SYZFGP7jjkuSI4f7Xvpt0S9c= +github.com/linckode/circl v1.3.71 h1:/TQQSpJ6PWtUb9G45trTvM/OtEEzchBN5j7/+KqjR4o= +github.com/linckode/circl v1.3.71/go.mod h1:dLQ5MZBjeiL72xd7hsKV+MmYrI0m07e/ZFzGB18L4yg= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/qubic/go-node-connector v0.5.0 h1:Z6TE/Ter28GZPreM6xczkGTuocqENXG1ka4mYs+ryRA= -github.com/qubic/go-node-connector v0.5.0/go.mod h1:iToCz9g9C0lWcah5gjnn16tQHdz6vvRNB0S7kKgARVU= -github.com/qubic/go-node-connector v0.6.1 h1:aBHLCmgg8qv+2R14XXd+i+FwcS6ttwp+4r7H9A54isQ= -github.com/qubic/go-node-connector v0.6.1/go.mod h1:iToCz9g9C0lWcah5gjnn16tQHdz6vvRNB0S7kKgARVU= +github.com/qubic/go-node-connector v0.6.2 h1:g2KrmoN+hH+pUXGup3KKN7lvOLpiN0RV4EaDtWJ8Ovs= +github.com/qubic/go-node-connector v0.6.2/go.mod h1:iToCz9g9C0lWcah5gjnn16tQHdz6vvRNB0S7kKgARVU= +github.com/qubic/go-schnorrq v1.0.0 h1:EiCC3v9v3esFFfhKNEGdAI4DFIY3Dm/wbH327pC5qco= +github.com/qubic/go-schnorrq v1.0.0/go.mod h1:KW64PcvyF4+cBA22pCx9wcoKDqPIbGz0EZ9dCZWV6Yo= github.com/silenceper/pool v1.0.0 h1:JTCaA+U6hJAA0P8nCx+JfsRCHMwLTfatsm5QXelffmU= github.com/silenceper/pool v1.0.0/go.mod h1:3DN13bqAbq86Lmzf6iUXWEPIWFPOSYVfaoceFvilKKI= github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=