mirror of
				https://github.com/XRPLF/clio.git
				synced 2025-11-04 11:55:51 +00:00 
			
		
		
		
	feat: Snapshot exporting tool (#1877)
In this PR: 1 We create a golang grpc client to request data from rippled 2 We store the data into the specific place 3 Add unittests 4 Create build script, the build can be initiated by set conan option `snapshot` being true. Please ignore the grpc server part. It will be implemented in importing tool.
This commit is contained in:
		
							
								
								
									
										66
									
								
								tools/snapshot/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								tools/snapshot/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,66 @@
 | 
			
		||||
# Build snapshot tool
 | 
			
		||||
# Need to be installed before building: go protoc-gen-go protoc-gen-go-grpc
 | 
			
		||||
 | 
			
		||||
set(GO_EXECUTABLE "go")
 | 
			
		||||
set(GO_SOURCE_DIR "${CMAKE_SOURCE_DIR}/tools/snapshot")
 | 
			
		||||
set(PROTO_INC_DIR "${xrpl_PACKAGE_FOLDER_RELEASE}/include/xrpl/proto")
 | 
			
		||||
set(PROTO_SOURCE_DIR "${PROTO_INC_DIR}/org/xrpl/rpc/v1/")
 | 
			
		||||
set(GO_OUTPUT "${CMAKE_BINARY_DIR}/clio_snapshot")
 | 
			
		||||
 | 
			
		||||
set(PROTO_FILES
 | 
			
		||||
    ${PROTO_SOURCE_DIR}/xrp_ledger.proto ${PROTO_SOURCE_DIR}/ledger.proto ${PROTO_SOURCE_DIR}/get_ledger.proto
 | 
			
		||||
    ${PROTO_SOURCE_DIR}/get_ledger_entry.proto ${PROTO_SOURCE_DIR}/get_ledger_data.proto
 | 
			
		||||
    ${PROTO_SOURCE_DIR}/get_ledger_diff.proto
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
execute_process(COMMAND go env GOPATH OUTPUT_VARIABLE GOPATH_VALUE OUTPUT_STRIP_TRAILING_WHITESPACE)
 | 
			
		||||
 | 
			
		||||
# Target Go package path
 | 
			
		||||
set(GO_IMPORT_PATH "org/xrpl/rpc/v1")
 | 
			
		||||
 | 
			
		||||
# Initialize the options strings
 | 
			
		||||
set(GO_OPTS "")
 | 
			
		||||
set(GRPC_OPTS "")
 | 
			
		||||
 | 
			
		||||
# Loop through each proto file
 | 
			
		||||
foreach(proto ${PROTO_FILES})
 | 
			
		||||
    get_filename_component(proto_filename ${proto} NAME)
 | 
			
		||||
 | 
			
		||||
    # Build the --go_opt and --go-grpc_opt mappings
 | 
			
		||||
    set(GO_OPTS ${GO_OPTS} --go_opt=M${GO_IMPORT_PATH}/${proto_filename}=${GO_IMPORT_PATH})
 | 
			
		||||
    set(GRPC_OPTS ${GRPC_OPTS} --go-grpc_opt=M${GO_IMPORT_PATH}/${proto_filename}=${GO_IMPORT_PATH})
 | 
			
		||||
endforeach()
 | 
			
		||||
 | 
			
		||||
foreach (proto ${PROTO_FILES})
 | 
			
		||||
  get_filename_component(proto_name ${proto} NAME_WE)
 | 
			
		||||
  add_custom_command(
 | 
			
		||||
    OUTPUT ${GO_SOURCE_DIR}/${proto_name}.pb.go
 | 
			
		||||
    COMMAND
 | 
			
		||||
      protoc ${GO_OPTS} ${GRPC_OPTS} 
 | 
			
		||||
      --go-grpc_out=${GO_SOURCE_DIR} -I${PROTO_INC_DIR} ${proto} --plugin=${GOPATH_VALUE}/bin/protoc-gen-go
 | 
			
		||||
      --plugin=${GOPATH_VALUE}/bin/protoc-gen-go-grpc --go_out=${GO_SOURCE_DIR}/
 | 
			
		||||
    DEPENDS ${proto}
 | 
			
		||||
    COMMENT "Generating Go code for ${proto}"
 | 
			
		||||
    VERBATIM
 | 
			
		||||
  )
 | 
			
		||||
 | 
			
		||||
  list(APPEND GENERATED_GO_FILES ${GO_SOURCE_DIR}/${proto_name}.pb.go)
 | 
			
		||||
endforeach ()
 | 
			
		||||
 | 
			
		||||
add_custom_target(build_clio_snapshot ALL DEPENDS run_go_tests ${GO_OUTPUT})
 | 
			
		||||
 | 
			
		||||
add_custom_target(run_go_tests
 | 
			
		||||
    COMMAND go test ./...
 | 
			
		||||
    WORKING_DIRECTORY ${GO_SOURCE_DIR}
 | 
			
		||||
    COMMENT "Running clio_snapshot unittests"
 | 
			
		||||
    VERBATIM
 | 
			
		||||
    DEPENDS ${GENERATED_GO_FILES}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
add_custom_command(
 | 
			
		||||
  OUTPUT ${GO_OUTPUT}
 | 
			
		||||
  COMMAND ${GO_EXECUTABLE} build -o ${GO_OUTPUT} ${GO_SOURCE_DIR}
 | 
			
		||||
  WORKING_DIRECTORY ${GO_SOURCE_DIR}
 | 
			
		||||
  COMMENT "Building clio_snapshot"
 | 
			
		||||
  VERBATIM
 | 
			
		||||
)
 | 
			
		||||
							
								
								
									
										23
									
								
								tools/snapshot/go.mod
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								tools/snapshot/go.mod
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,23 @@
 | 
			
		||||
module xrplf/clio/clio_snapshot
 | 
			
		||||
 | 
			
		||||
go 1.22
 | 
			
		||||
 | 
			
		||||
toolchain go1.22.11
 | 
			
		||||
 | 
			
		||||
require (
 | 
			
		||||
	github.com/golang/mock v1.6.0
 | 
			
		||||
	github.com/spf13/pflag v1.0.6
 | 
			
		||||
	github.com/stretchr/testify v1.10.0
 | 
			
		||||
	google.golang.org/grpc v1.69.4
 | 
			
		||||
	google.golang.org/protobuf v1.36.3
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
require (
 | 
			
		||||
	github.com/davecgh/go-spew v1.1.1 // indirect
 | 
			
		||||
	github.com/pmezard/go-difflib v1.0.0 // indirect
 | 
			
		||||
	golang.org/x/net v0.30.0 // indirect
 | 
			
		||||
	golang.org/x/sys v0.26.0 // indirect
 | 
			
		||||
	golang.org/x/text v0.19.0 // indirect
 | 
			
		||||
	google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53 // indirect
 | 
			
		||||
	gopkg.in/yaml.v3 v3.0.1 // indirect
 | 
			
		||||
)
 | 
			
		||||
							
								
								
									
										69
									
								
								tools/snapshot/go.sum
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								tools/snapshot/go.sum
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,69 @@
 | 
			
		||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 | 
			
		||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 | 
			
		||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
 | 
			
		||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
 | 
			
		||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
 | 
			
		||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
 | 
			
		||||
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
 | 
			
		||||
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
 | 
			
		||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
 | 
			
		||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
 | 
			
		||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
 | 
			
		||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 | 
			
		||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
 | 
			
		||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 | 
			
		||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 | 
			
		||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 | 
			
		||||
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
 | 
			
		||||
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
 | 
			
		||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
 | 
			
		||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
 | 
			
		||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
 | 
			
		||||
go.opentelemetry.io/otel v1.31.0 h1:NsJcKPIW0D0H3NgzPDHmo0WW6SptzPdqg/L1zsIm2hY=
 | 
			
		||||
go.opentelemetry.io/otel v1.31.0/go.mod h1:O0C14Yl9FgkjqcCZAsE053C13OaddMYr/hz6clDkEJE=
 | 
			
		||||
go.opentelemetry.io/otel/metric v1.31.0 h1:FSErL0ATQAmYHUIzSezZibnyVlft1ybhy4ozRPcF2fE=
 | 
			
		||||
go.opentelemetry.io/otel/metric v1.31.0/go.mod h1:C3dEloVbLuYoX41KpmAhOqNriGbA+qqH6PQ5E5mUfnY=
 | 
			
		||||
go.opentelemetry.io/otel/sdk v1.31.0 h1:xLY3abVHYZ5HSfOg3l2E5LUj2Cwva5Y7yGxnSW9H5Gk=
 | 
			
		||||
go.opentelemetry.io/otel/sdk v1.31.0/go.mod h1:TfRbMdhvxIIr/B2N2LQW2S5v9m3gOQ/08KsbbO5BPT0=
 | 
			
		||||
go.opentelemetry.io/otel/sdk/metric v1.31.0 h1:i9hxxLJF/9kkvfHppyLL55aW7iIJz4JjxTeYusH7zMc=
 | 
			
		||||
go.opentelemetry.io/otel/sdk/metric v1.31.0/go.mod h1:CRInTMVvNhUKgSAMbKyTMxqOBC0zgyxzW55lZzX43Y8=
 | 
			
		||||
go.opentelemetry.io/otel/trace v1.31.0 h1:ffjsj1aRouKewfr85U2aGagJ46+MvodynlQ1HYdmJys=
 | 
			
		||||
go.opentelemetry.io/otel/trace v1.31.0/go.mod h1:TXZkRk7SM2ZQLtR6eoAWQFIHPvzQ06FJAsO1tJg480A=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 | 
			
		||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 | 
			
		||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 | 
			
		||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 | 
			
		||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
 | 
			
		||||
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
 | 
			
		||||
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
 | 
			
		||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
			
		||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
 | 
			
		||||
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 | 
			
		||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 | 
			
		||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 | 
			
		||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 | 
			
		||||
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
 | 
			
		||||
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
 | 
			
		||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 | 
			
		||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 | 
			
		||||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
 | 
			
		||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 | 
			
		||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 | 
			
		||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 | 
			
		||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53 h1:X58yt85/IXCx0Y3ZwN6sEIKZzQtDEYaBWrDvErdXrRE=
 | 
			
		||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI=
 | 
			
		||||
google.golang.org/grpc v1.69.4 h1:MF5TftSMkd8GLw/m0KM6V8CMOCY6NZ1NQDPGFgbTt4A=
 | 
			
		||||
google.golang.org/grpc v1.69.4/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4=
 | 
			
		||||
google.golang.org/protobuf v1.36.3 h1:82DV7MYdb8anAVi3qge1wSnMDrnKK7ebr+I0hHRN1BU=
 | 
			
		||||
google.golang.org/protobuf v1.36.3/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
 | 
			
		||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
 | 
			
		||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
			
		||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 | 
			
		||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 | 
			
		||||
							
								
								
									
										245
									
								
								tools/snapshot/internal/export/export.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										245
									
								
								tools/snapshot/internal/export/export.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,245 @@
 | 
			
		||||
package export
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"log"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"sync"
 | 
			
		||||
	pb "xrplf/clio/clio_snapshot/org/xrpl/rpc/v1"
 | 
			
		||||
 | 
			
		||||
	"google.golang.org/grpc"
 | 
			
		||||
	"google.golang.org/grpc/credentials/insecure"
 | 
			
		||||
	"google.golang.org/protobuf/proto"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	deltaDataFolderDiv = 10000
 | 
			
		||||
	grpcUser           = "clio-snapshot"
 | 
			
		||||
	markerNum          = 16
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type gRPCClient struct {
 | 
			
		||||
	Client pb.XRPLedgerAPIServiceClient
 | 
			
		||||
	conn   *grpc.ClientConn
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *gRPCClient) Close() error {
 | 
			
		||||
	return c.conn.Close()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func createGRPCClient(serverAddr string) (*gRPCClient, error) {
 | 
			
		||||
	opts := []grpc.DialOption{grpc.WithTransportCredentials(insecure.NewCredentials())}
 | 
			
		||||
 | 
			
		||||
	conn, err := grpc.NewClient(serverAddr, opts...)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("Failed to dial: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	client := pb.NewXRPLedgerAPIServiceClient(conn)
 | 
			
		||||
	return &gRPCClient{
 | 
			
		||||
		Client: client,
 | 
			
		||||
		conn:   conn,
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getLedgerDeltaData(client pb.XRPLedgerAPIServiceClient, seq uint32, path string) {
 | 
			
		||||
	ctx, cancel := context.WithCancel(context.Background())
 | 
			
		||||
	defer cancel()
 | 
			
		||||
 | 
			
		||||
	request := pb.GetLedgerRequest{}
 | 
			
		||||
	ledger := &pb.LedgerSpecifier{
 | 
			
		||||
		Ledger: &pb.LedgerSpecifier_Sequence{
 | 
			
		||||
			Sequence: seq,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	request.Ledger = ledger
 | 
			
		||||
	request.User = grpcUser
 | 
			
		||||
	request.GetObjectNeighbors = true
 | 
			
		||||
	request.Transactions = true
 | 
			
		||||
	request.Expand = true
 | 
			
		||||
	request.GetObjects = true
 | 
			
		||||
 | 
			
		||||
	response, err := client.GetLedger(ctx, &request)
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatalf("Error getting ledger data: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	saveLedgerDeltaData(seq, response, path)
 | 
			
		||||
 | 
			
		||||
	log.Printf("Processing delta sequence: %d\n", seq)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func roundDown(n uint32, roundTo uint32) uint32 {
 | 
			
		||||
	if roundTo == 0 {
 | 
			
		||||
		return n
 | 
			
		||||
	}
 | 
			
		||||
	return n - (n % roundTo)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func saveLedgerDeltaData(seq uint32, response *pb.GetLedgerResponse, path string) {
 | 
			
		||||
	subPath := filepath.Join(path, fmt.Sprintf("ledger_diff_%d", roundDown(seq, deltaDataFolderDiv)))
 | 
			
		||||
	err := os.MkdirAll(subPath, os.ModePerm)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatalf("Error creating directory: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	protoData, err := proto.Marshal(response)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatalf("Error marshalling data: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	filePath := filepath.Join(subPath, fmt.Sprintf("%d.dat", seq))
 | 
			
		||||
 | 
			
		||||
	err = os.WriteFile(filePath, protoData, 0644)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatalf("failed to write file: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func generateMarkers(markerNum uint32) [][32]byte {
 | 
			
		||||
	var byteArray [32]byte
 | 
			
		||||
 | 
			
		||||
	incr := 256 / markerNum
 | 
			
		||||
 | 
			
		||||
	var byteArrayList [][32]byte
 | 
			
		||||
 | 
			
		||||
	for i := 0; i < int(markerNum); i++ {
 | 
			
		||||
		byteArray[0] = byte(i * int(incr)) // Increment the highest byte
 | 
			
		||||
		byteArrayList = append(byteArrayList, byteArray)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return byteArrayList
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func saveLedgerData(path string, data *pb.GetLedgerDataResponse) {
 | 
			
		||||
	protoData, err := proto.Marshal(data)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatalf("Error marshalling data: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = os.WriteFile(path, protoData, 0644)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatalf("failed to write file: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getLedgerData(client pb.XRPLedgerAPIServiceClient, seq uint32, marker []byte, end []byte, path string) {
 | 
			
		||||
	ctx, cancel := context.WithCancel(context.Background())
 | 
			
		||||
	defer cancel()
 | 
			
		||||
 | 
			
		||||
	request := pb.GetLedgerDataRequest{}
 | 
			
		||||
	ledger := &pb.LedgerSpecifier{
 | 
			
		||||
		Ledger: &pb.LedgerSpecifier_Sequence{
 | 
			
		||||
			Sequence: seq,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	request.Ledger = ledger
 | 
			
		||||
	request.Marker = marker[:]
 | 
			
		||||
	if end != nil {
 | 
			
		||||
		request.EndMarker = end[:]
 | 
			
		||||
	}
 | 
			
		||||
	request.User = grpcUser
 | 
			
		||||
 | 
			
		||||
	subPath := filepath.Join(path, fmt.Sprintf("ledger_data_%d", seq), fmt.Sprintf("marker_%x", marker))
 | 
			
		||||
	err := os.MkdirAll(subPath, os.ModePerm)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatalf("Error creating directory: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for request.Marker != nil {
 | 
			
		||||
		res, err := client.GetLedgerData(ctx, &request)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			log.Fatalf("Error getting ledger data: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		filePath := filepath.Join(subPath, fmt.Sprintf("%x.dat", request.Marker))
 | 
			
		||||
		saveLedgerData(filePath, res)
 | 
			
		||||
		request.Marker = res.Marker
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getLedgerFullData(client pb.XRPLedgerAPIServiceClient, seq uint32, path string) {
 | 
			
		||||
	log.Printf("Processing full sequence: %d\n", seq)
 | 
			
		||||
 | 
			
		||||
	markers := generateMarkers(markerNum)
 | 
			
		||||
 | 
			
		||||
	var wg sync.WaitGroup
 | 
			
		||||
 | 
			
		||||
	// Launch a goroutine for each marker
 | 
			
		||||
	for i, marker := range markers {
 | 
			
		||||
		wg.Add(1)
 | 
			
		||||
		var end []byte
 | 
			
		||||
		if i != len(markers)-1 {
 | 
			
		||||
			end = markers[i+1][:]
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		fmt.Printf("Got ledger data marker: %x-%x\n", marker, end)
 | 
			
		||||
 | 
			
		||||
		go func() {
 | 
			
		||||
			defer wg.Done()
 | 
			
		||||
			getLedgerData(client, seq, marker[:], end, path)
 | 
			
		||||
		}()
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	wg.Wait()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func checkPath(path string) {
 | 
			
		||||
	if _, err := os.Stat(path); os.IsNotExist(err) {
 | 
			
		||||
		// Create the directory if it doesn't exist
 | 
			
		||||
		err := os.MkdirAll(path, os.ModePerm)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			log.Fatalf("Error creating directory: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ExportFromFullLedger(grpcServer string, startSeq uint32, endSeq uint32, path string) {
 | 
			
		||||
	checkPath(path)
 | 
			
		||||
 | 
			
		||||
	client, err := createGRPCClient(grpcServer)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatalf("Error creating gRPC client: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	defer client.Close()
 | 
			
		||||
 | 
			
		||||
	exportFromFullLedgerImpl(client.Client, startSeq, endSeq, path)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func exportFromFullLedgerImpl(client pb.XRPLedgerAPIServiceClient, startSeq uint32, endSeq uint32, path string) {
 | 
			
		||||
 | 
			
		||||
	getLedgerFullData(client, startSeq, path)
 | 
			
		||||
 | 
			
		||||
	//We need to fetch the ledger header and txs for startSeq as well
 | 
			
		||||
	for i := startSeq; i <= endSeq; i++ {
 | 
			
		||||
		getLedgerDeltaData(client, i, path)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	log.Printf("Exporting from full ledger: %d to %d at path %s\n", startSeq, endSeq, path)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ExportFromDeltaLedger(grpcServer string, startSeq uint32, endSeq uint32, path string) {
 | 
			
		||||
	checkPath(path)
 | 
			
		||||
 | 
			
		||||
	client, err := createGRPCClient(grpcServer)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatalf("Error creating gRPC client: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	defer client.Close()
 | 
			
		||||
 | 
			
		||||
	exportFromDeltaLedgerImpl(client.Client, startSeq, endSeq, path)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func exportFromDeltaLedgerImpl(client pb.XRPLedgerAPIServiceClient, startSeq uint32, endSeq uint32, path string) {
 | 
			
		||||
	for i := startSeq; i <= endSeq; i++ {
 | 
			
		||||
		getLedgerDeltaData(client, i, path)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	log.Printf("Exporting from ledger: %d to %d at path %s\n", startSeq, endSeq, path)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										153
									
								
								tools/snapshot/internal/export/export_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										153
									
								
								tools/snapshot/internal/export/export_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,153 @@
 | 
			
		||||
package export
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"os"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"github.com/stretchr/testify/assert"
 | 
			
		||||
 | 
			
		||||
	"xrplf/clio/clio_snapshot/mocks"
 | 
			
		||||
	pb "xrplf/clio/clio_snapshot/org/xrpl/rpc/v1"
 | 
			
		||||
 | 
			
		||||
	"github.com/golang/mock/gomock"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestExportDeltaLedgerData(t *testing.T) {
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name     string
 | 
			
		||||
		startSeq uint32
 | 
			
		||||
		endSeq   uint32
 | 
			
		||||
	}{
 | 
			
		||||
		{"OneSeq", 1, 1},
 | 
			
		||||
		{"MultipleSeq", 1, 20},
 | 
			
		||||
		{"EndSeqLessThanStartSeq", 20, 1},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
			ctrl := gomock.NewController(t)
 | 
			
		||||
			defer ctrl.Finish()
 | 
			
		||||
 | 
			
		||||
			mockClient := mocks.NewMockXRPLedgerAPIServiceClient(ctrl)
 | 
			
		||||
 | 
			
		||||
			mockResponse := &pb.GetLedgerResponse{}
 | 
			
		||||
 | 
			
		||||
			times := tt.endSeq - tt.startSeq + 1
 | 
			
		||||
			if tt.endSeq < tt.startSeq {
 | 
			
		||||
				times = 0
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			mockClient.EXPECT().GetLedger(gomock.Any(), gomock.Any()).Return(mockResponse, nil).Times(int(times))
 | 
			
		||||
 | 
			
		||||
			defer os.RemoveAll("test")
 | 
			
		||||
 | 
			
		||||
			exportFromDeltaLedgerImpl(mockClient, tt.startSeq, tt.endSeq, "test")
 | 
			
		||||
 | 
			
		||||
			_, err := os.Stat("test")
 | 
			
		||||
 | 
			
		||||
			assert.Equal(t, os.IsNotExist(err), tt.endSeq < tt.startSeq)
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestExportFullLedgerData(t *testing.T) {
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name     string
 | 
			
		||||
		startSeq uint32
 | 
			
		||||
		endSeq   uint32
 | 
			
		||||
	}{
 | 
			
		||||
		{"OneSeq", 1, 1},
 | 
			
		||||
		{"MultipleSeq", 1, 20},
 | 
			
		||||
		{"EndSeqLessThanStartSeq", 20, 1},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
			ctrl := gomock.NewController(t)
 | 
			
		||||
			defer ctrl.Finish()
 | 
			
		||||
 | 
			
		||||
			mockClient := mocks.NewMockXRPLedgerAPIServiceClient(ctrl)
 | 
			
		||||
 | 
			
		||||
			mockDataResponse := &pb.GetLedgerDataResponse{}
 | 
			
		||||
			mockLedgerResponse := &pb.GetLedgerResponse{}
 | 
			
		||||
 | 
			
		||||
			timesLedgerDataCalled := 16
 | 
			
		||||
			timesLedgerCalled := tt.endSeq - tt.startSeq + 1
 | 
			
		||||
 | 
			
		||||
			if tt.endSeq < tt.startSeq {
 | 
			
		||||
				timesLedgerCalled = 0
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			mockClient.EXPECT().GetLedgerData(gomock.Any(), gomock.Any()).Return(mockDataResponse, nil).Times(timesLedgerDataCalled)
 | 
			
		||||
 | 
			
		||||
			mockClient.EXPECT().GetLedger(gomock.Any(), gomock.Any()).Return(mockLedgerResponse, nil).Times(int(timesLedgerCalled))
 | 
			
		||||
 | 
			
		||||
			defer os.RemoveAll("test")
 | 
			
		||||
			exportFromFullLedgerImpl(mockClient, tt.startSeq, tt.endSeq, "test")
 | 
			
		||||
 | 
			
		||||
			_, err := os.Stat("test")
 | 
			
		||||
 | 
			
		||||
			assert.False(t, os.IsNotExist(err))
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestRoundDown(t *testing.T) {
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name string
 | 
			
		||||
		in1  uint32
 | 
			
		||||
		in2  uint32
 | 
			
		||||
		out  uint32
 | 
			
		||||
	}{
 | 
			
		||||
		{"RoundDownToZero", 10, 0, 10},
 | 
			
		||||
		{"RoundDown12To10", 12, 10, 10},
 | 
			
		||||
		{"RoundDownToOne", 13, 1, 13},
 | 
			
		||||
		{"RoundDown100", 103, 100, 100},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
			assert.Equal(t, roundDown(tt.in1, tt.in2), tt.out)
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestGenerateMarkers(t *testing.T) {
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name string
 | 
			
		||||
		in   uint32
 | 
			
		||||
		out  [][32]byte
 | 
			
		||||
	}{
 | 
			
		||||
		{"GenerateMarkers1", 1, [][32]byte{{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}},
 | 
			
		||||
		{"GenerateMarkers2", 2, [][32]byte{{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
 | 
			
		||||
			{0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}},
 | 
			
		||||
		{"GenerateMarkers4", 4, [][32]byte{{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
 | 
			
		||||
			{0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
 | 
			
		||||
			{0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
 | 
			
		||||
			{0xc0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}},
 | 
			
		||||
	}
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
			assert.Equal(t, generateMarkers(tt.in), tt.out)
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestCheckPath(t *testing.T) {
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name string
 | 
			
		||||
		path string
 | 
			
		||||
	}{
 | 
			
		||||
		{"Path", "test"},
 | 
			
		||||
		{"NestedPath", "test/test"}}
 | 
			
		||||
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
			checkPath(tt.path)
 | 
			
		||||
			defer os.RemoveAll(tt.path)
 | 
			
		||||
			_, err := os.Stat(tt.path)
 | 
			
		||||
			assert.False(t, os.IsNotExist(err))
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										63
									
								
								tools/snapshot/internal/parse_args/parse_args.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								tools/snapshot/internal/parse_args/parse_args.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,63 @@
 | 
			
		||||
package parse_args
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"os"
 | 
			
		||||
 | 
			
		||||
	flag "github.com/spf13/pflag"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Args struct {
 | 
			
		||||
	ExportMode string
 | 
			
		||||
	StartSeq   uint32
 | 
			
		||||
	EndSeq     uint32
 | 
			
		||||
	Path       string
 | 
			
		||||
	GrpcServer string
 | 
			
		||||
	ServerMode bool
 | 
			
		||||
	GrpcPort   uint32
 | 
			
		||||
	WsPort     uint32
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Parse() (*Args, error) {
 | 
			
		||||
 | 
			
		||||
	fs := flag.NewFlagSet(os.Args[0], flag.ContinueOnError)
 | 
			
		||||
	fs.Usage = PrintUsage
 | 
			
		||||
 | 
			
		||||
	exportMode := fs.StringP("export", "e", "", "Set export mode: 'full' (export full ledger data of start_seq) or 'delta' (only export ledger diff data)")
 | 
			
		||||
	seq := fs.Uint32("start_seq", 0, "Starting sequence number")
 | 
			
		||||
	endSeq := fs.Uint32("end_seq", 0, "Ending sequence number")
 | 
			
		||||
	path := fs.StringP("path", "p", "", "Path to the data")
 | 
			
		||||
	grpcServer := fs.StringP("grpc_server", "g", "localhost:50051", "rippled's gRPC server address")
 | 
			
		||||
	serverMode := fs.BoolP("server", "s", false, "Start server mode")
 | 
			
		||||
	grpcPort := fs.Uint32("grpc_port", 0, "Port for gRPC server to listen on")
 | 
			
		||||
	wsPort := fs.Uint32("ws_port", 0, "Port for WebSocket server to listen on")
 | 
			
		||||
	fs.Parse(os.Args[1:])
 | 
			
		||||
 | 
			
		||||
	if *serverMode && *exportMode != "" {
 | 
			
		||||
		return nil, fmt.Errorf("Invalid usage: --server and --export cannot be used at the same time.")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if *serverMode {
 | 
			
		||||
		if *grpcPort == 0 || *wsPort == 0 || *path == "" {
 | 
			
		||||
			return nil, fmt.Errorf("Invalid usage: --grpc_port and --ws_port and --path are required for server mode.")
 | 
			
		||||
		}
 | 
			
		||||
	} else if *exportMode != "" {
 | 
			
		||||
		if *exportMode == "full" || *exportMode == "delta" {
 | 
			
		||||
			if *seq == 0 || *endSeq == 0 || *path == "" || *grpcServer == "" {
 | 
			
		||||
				return nil, fmt.Errorf("Invalid usage: --start_seq, --end_seq, --grpc_server and --path are required for export")
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			return nil, fmt.Errorf("Invalid usage: Invalid export mode. Use 'full' or 'delta'.")
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		return nil, fmt.Errorf("Invalid usage: --export or --server flag is required.")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &Args{*exportMode, *seq, *endSeq, *path, *grpcServer, *serverMode, *grpcPort, *wsPort}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func PrintUsage() {
 | 
			
		||||
	fmt.Println("Usage: clio_snapshot [options]")
 | 
			
		||||
	fmt.Println("Options:")
 | 
			
		||||
	flag.PrintDefaults()
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										94
									
								
								tools/snapshot/internal/parse_args/parse_args_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								tools/snapshot/internal/parse_args/parse_args_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,94 @@
 | 
			
		||||
package parse_args
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"flag"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"os"
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"testing"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestParse(t *testing.T) {
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name       string
 | 
			
		||||
		args       []string
 | 
			
		||||
		want       *Args
 | 
			
		||||
		expectErr  bool
 | 
			
		||||
		errMessage string
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name: "Valid export full mode",
 | 
			
		||||
			args: []string{"cmd", "--export=full", "--start_seq=1", "--end_seq=10", "--path=/data", "--grpc_server=localhost:50051"},
 | 
			
		||||
			want: &Args{
 | 
			
		||||
				ExportMode: "full",
 | 
			
		||||
				StartSeq:   1,
 | 
			
		||||
				EndSeq:     10,
 | 
			
		||||
				Path:       "/data",
 | 
			
		||||
				GrpcServer: "localhost:50051",
 | 
			
		||||
				ServerMode: false,
 | 
			
		||||
			},
 | 
			
		||||
			expectErr: false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:       "Missing required flags in export mode",
 | 
			
		||||
			args:       []string{"cmd", "--export=delta", "--start_seq=1"},
 | 
			
		||||
			want:       nil,
 | 
			
		||||
			expectErr:  true,
 | 
			
		||||
			errMessage: "Invalid usage: --start_seq, --end_seq, --grpc_server and --path are required for export",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:       "Invalid export mode",
 | 
			
		||||
			args:       []string{"cmd", "--export=invalid"},
 | 
			
		||||
			want:       nil,
 | 
			
		||||
			expectErr:  true,
 | 
			
		||||
			errMessage: "Invalid usage: Invalid export mode. Use 'full' or 'delta'.",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "Server mode with default grpc server flags",
 | 
			
		||||
			args: []string{"cmd", "--server", "--ws_port=1234", "--grpc_port=22", "--path=/server_data"},
 | 
			
		||||
			want: &Args{
 | 
			
		||||
				ServerMode: true,
 | 
			
		||||
				GrpcPort:   22,
 | 
			
		||||
				WsPort:     1234,
 | 
			
		||||
				StartSeq:   0,
 | 
			
		||||
				EndSeq:     0,
 | 
			
		||||
				Path:       "/server_data",
 | 
			
		||||
				GrpcServer: "localhost:50051",
 | 
			
		||||
			},
 | 
			
		||||
			expectErr: false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:       "Server and export mode together (error)",
 | 
			
		||||
			args:       []string{"cmd", "--server", "--export=full"},
 | 
			
		||||
			want:       nil,
 | 
			
		||||
			expectErr:  true,
 | 
			
		||||
			errMessage: "Invalid usage: --server and --export cannot be used at the same time.",
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
			flag.CommandLine = flag.NewFlagSet(os.Args[1], flag.ExitOnError)
 | 
			
		||||
 | 
			
		||||
			os.Args = tt.args
 | 
			
		||||
 | 
			
		||||
			fmt.Println("Running test with args:", tt.args)
 | 
			
		||||
			got, err := Parse()
 | 
			
		||||
 | 
			
		||||
			if tt.expectErr {
 | 
			
		||||
				if err == nil {
 | 
			
		||||
					t.Errorf("Expected error but got none")
 | 
			
		||||
				} else if err.Error() != tt.errMessage {
 | 
			
		||||
					t.Errorf("Expected error message '%s', got '%s'", tt.errMessage, err.Error())
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					t.Errorf("Unexpected error: %v", err)
 | 
			
		||||
				}
 | 
			
		||||
				if !reflect.DeepEqual(got, tt.want) {
 | 
			
		||||
					t.Errorf("Expected %+v, got %+v", tt.want, got)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										247
									
								
								tools/snapshot/mocks/mock_grpc_server.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										247
									
								
								tools/snapshot/mocks/mock_grpc_server.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,247 @@
 | 
			
		||||
// Code generated by MockGen. DO NOT EDIT.
 | 
			
		||||
// Source: org/xrpl/rpc/v1/xrp_ledger_grpc.pb.go
 | 
			
		||||
 | 
			
		||||
// Package mocks is a generated GoMock package.
 | 
			
		||||
package mocks
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	context "context"
 | 
			
		||||
	reflect "reflect"
 | 
			
		||||
	v1 "xrplf/clio/clio_snapshot/org/xrpl/rpc/v1"
 | 
			
		||||
 | 
			
		||||
	gomock "github.com/golang/mock/gomock"
 | 
			
		||||
	grpc "google.golang.org/grpc"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// MockXRPLedgerAPIServiceClient is a mock of XRPLedgerAPIServiceClient interface.
 | 
			
		||||
type MockXRPLedgerAPIServiceClient struct {
 | 
			
		||||
	ctrl     *gomock.Controller
 | 
			
		||||
	recorder *MockXRPLedgerAPIServiceClientMockRecorder
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MockXRPLedgerAPIServiceClientMockRecorder is the mock recorder for MockXRPLedgerAPIServiceClient.
 | 
			
		||||
type MockXRPLedgerAPIServiceClientMockRecorder struct {
 | 
			
		||||
	mock *MockXRPLedgerAPIServiceClient
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewMockXRPLedgerAPIServiceClient creates a new mock instance.
 | 
			
		||||
func NewMockXRPLedgerAPIServiceClient(ctrl *gomock.Controller) *MockXRPLedgerAPIServiceClient {
 | 
			
		||||
	mock := &MockXRPLedgerAPIServiceClient{ctrl: ctrl}
 | 
			
		||||
	mock.recorder = &MockXRPLedgerAPIServiceClientMockRecorder{mock}
 | 
			
		||||
	return mock
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// EXPECT returns an object that allows the caller to indicate expected use.
 | 
			
		||||
func (m *MockXRPLedgerAPIServiceClient) EXPECT() *MockXRPLedgerAPIServiceClientMockRecorder {
 | 
			
		||||
	return m.recorder
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetLedger mocks base method.
 | 
			
		||||
func (m *MockXRPLedgerAPIServiceClient) GetLedger(ctx context.Context, in *v1.GetLedgerRequest, opts ...grpc.CallOption) (*v1.GetLedgerResponse, error) {
 | 
			
		||||
	m.ctrl.T.Helper()
 | 
			
		||||
	varargs := []interface{}{ctx, in}
 | 
			
		||||
	for _, a := range opts {
 | 
			
		||||
		varargs = append(varargs, a)
 | 
			
		||||
	}
 | 
			
		||||
	ret := m.ctrl.Call(m, "GetLedger", varargs...)
 | 
			
		||||
	ret0, _ := ret[0].(*v1.GetLedgerResponse)
 | 
			
		||||
	ret1, _ := ret[1].(error)
 | 
			
		||||
	return ret0, ret1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetLedger indicates an expected call of GetLedger.
 | 
			
		||||
func (mr *MockXRPLedgerAPIServiceClientMockRecorder) GetLedger(ctx, in interface{}, opts ...interface{}) *gomock.Call {
 | 
			
		||||
	mr.mock.ctrl.T.Helper()
 | 
			
		||||
	varargs := append([]interface{}{ctx, in}, opts...)
 | 
			
		||||
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLedger", reflect.TypeOf((*MockXRPLedgerAPIServiceClient)(nil).GetLedger), varargs...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetLedgerData mocks base method.
 | 
			
		||||
func (m *MockXRPLedgerAPIServiceClient) GetLedgerData(ctx context.Context, in *v1.GetLedgerDataRequest, opts ...grpc.CallOption) (*v1.GetLedgerDataResponse, error) {
 | 
			
		||||
	m.ctrl.T.Helper()
 | 
			
		||||
	varargs := []interface{}{ctx, in}
 | 
			
		||||
	for _, a := range opts {
 | 
			
		||||
		varargs = append(varargs, a)
 | 
			
		||||
	}
 | 
			
		||||
	ret := m.ctrl.Call(m, "GetLedgerData", varargs...)
 | 
			
		||||
	ret0, _ := ret[0].(*v1.GetLedgerDataResponse)
 | 
			
		||||
	ret1, _ := ret[1].(error)
 | 
			
		||||
	return ret0, ret1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetLedgerData indicates an expected call of GetLedgerData.
 | 
			
		||||
func (mr *MockXRPLedgerAPIServiceClientMockRecorder) GetLedgerData(ctx, in interface{}, opts ...interface{}) *gomock.Call {
 | 
			
		||||
	mr.mock.ctrl.T.Helper()
 | 
			
		||||
	varargs := append([]interface{}{ctx, in}, opts...)
 | 
			
		||||
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLedgerData", reflect.TypeOf((*MockXRPLedgerAPIServiceClient)(nil).GetLedgerData), varargs...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetLedgerDiff mocks base method.
 | 
			
		||||
func (m *MockXRPLedgerAPIServiceClient) GetLedgerDiff(ctx context.Context, in *v1.GetLedgerDiffRequest, opts ...grpc.CallOption) (*v1.GetLedgerDiffResponse, error) {
 | 
			
		||||
	m.ctrl.T.Helper()
 | 
			
		||||
	varargs := []interface{}{ctx, in}
 | 
			
		||||
	for _, a := range opts {
 | 
			
		||||
		varargs = append(varargs, a)
 | 
			
		||||
	}
 | 
			
		||||
	ret := m.ctrl.Call(m, "GetLedgerDiff", varargs...)
 | 
			
		||||
	ret0, _ := ret[0].(*v1.GetLedgerDiffResponse)
 | 
			
		||||
	ret1, _ := ret[1].(error)
 | 
			
		||||
	return ret0, ret1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetLedgerDiff indicates an expected call of GetLedgerDiff.
 | 
			
		||||
func (mr *MockXRPLedgerAPIServiceClientMockRecorder) GetLedgerDiff(ctx, in interface{}, opts ...interface{}) *gomock.Call {
 | 
			
		||||
	mr.mock.ctrl.T.Helper()
 | 
			
		||||
	varargs := append([]interface{}{ctx, in}, opts...)
 | 
			
		||||
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLedgerDiff", reflect.TypeOf((*MockXRPLedgerAPIServiceClient)(nil).GetLedgerDiff), varargs...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetLedgerEntry mocks base method.
 | 
			
		||||
func (m *MockXRPLedgerAPIServiceClient) GetLedgerEntry(ctx context.Context, in *v1.GetLedgerEntryRequest, opts ...grpc.CallOption) (*v1.GetLedgerEntryResponse, error) {
 | 
			
		||||
	m.ctrl.T.Helper()
 | 
			
		||||
	varargs := []interface{}{ctx, in}
 | 
			
		||||
	for _, a := range opts {
 | 
			
		||||
		varargs = append(varargs, a)
 | 
			
		||||
	}
 | 
			
		||||
	ret := m.ctrl.Call(m, "GetLedgerEntry", varargs...)
 | 
			
		||||
	ret0, _ := ret[0].(*v1.GetLedgerEntryResponse)
 | 
			
		||||
	ret1, _ := ret[1].(error)
 | 
			
		||||
	return ret0, ret1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetLedgerEntry indicates an expected call of GetLedgerEntry.
 | 
			
		||||
func (mr *MockXRPLedgerAPIServiceClientMockRecorder) GetLedgerEntry(ctx, in interface{}, opts ...interface{}) *gomock.Call {
 | 
			
		||||
	mr.mock.ctrl.T.Helper()
 | 
			
		||||
	varargs := append([]interface{}{ctx, in}, opts...)
 | 
			
		||||
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLedgerEntry", reflect.TypeOf((*MockXRPLedgerAPIServiceClient)(nil).GetLedgerEntry), varargs...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MockXRPLedgerAPIServiceServer is a mock of XRPLedgerAPIServiceServer interface.
 | 
			
		||||
type MockXRPLedgerAPIServiceServer struct {
 | 
			
		||||
	ctrl     *gomock.Controller
 | 
			
		||||
	recorder *MockXRPLedgerAPIServiceServerMockRecorder
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MockXRPLedgerAPIServiceServerMockRecorder is the mock recorder for MockXRPLedgerAPIServiceServer.
 | 
			
		||||
type MockXRPLedgerAPIServiceServerMockRecorder struct {
 | 
			
		||||
	mock *MockXRPLedgerAPIServiceServer
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewMockXRPLedgerAPIServiceServer creates a new mock instance.
 | 
			
		||||
func NewMockXRPLedgerAPIServiceServer(ctrl *gomock.Controller) *MockXRPLedgerAPIServiceServer {
 | 
			
		||||
	mock := &MockXRPLedgerAPIServiceServer{ctrl: ctrl}
 | 
			
		||||
	mock.recorder = &MockXRPLedgerAPIServiceServerMockRecorder{mock}
 | 
			
		||||
	return mock
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// EXPECT returns an object that allows the caller to indicate expected use.
 | 
			
		||||
func (m *MockXRPLedgerAPIServiceServer) EXPECT() *MockXRPLedgerAPIServiceServerMockRecorder {
 | 
			
		||||
	return m.recorder
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetLedger mocks base method.
 | 
			
		||||
func (m *MockXRPLedgerAPIServiceServer) GetLedger(arg0 context.Context, arg1 *v1.GetLedgerRequest) (*v1.GetLedgerResponse, error) {
 | 
			
		||||
	m.ctrl.T.Helper()
 | 
			
		||||
	ret := m.ctrl.Call(m, "GetLedger", arg0, arg1)
 | 
			
		||||
	ret0, _ := ret[0].(*v1.GetLedgerResponse)
 | 
			
		||||
	ret1, _ := ret[1].(error)
 | 
			
		||||
	return ret0, ret1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetLedger indicates an expected call of GetLedger.
 | 
			
		||||
func (mr *MockXRPLedgerAPIServiceServerMockRecorder) GetLedger(arg0, arg1 interface{}) *gomock.Call {
 | 
			
		||||
	mr.mock.ctrl.T.Helper()
 | 
			
		||||
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLedger", reflect.TypeOf((*MockXRPLedgerAPIServiceServer)(nil).GetLedger), arg0, arg1)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetLedgerData mocks base method.
 | 
			
		||||
func (m *MockXRPLedgerAPIServiceServer) GetLedgerData(arg0 context.Context, arg1 *v1.GetLedgerDataRequest) (*v1.GetLedgerDataResponse, error) {
 | 
			
		||||
	m.ctrl.T.Helper()
 | 
			
		||||
	ret := m.ctrl.Call(m, "GetLedgerData", arg0, arg1)
 | 
			
		||||
	ret0, _ := ret[0].(*v1.GetLedgerDataResponse)
 | 
			
		||||
	ret1, _ := ret[1].(error)
 | 
			
		||||
	return ret0, ret1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetLedgerData indicates an expected call of GetLedgerData.
 | 
			
		||||
func (mr *MockXRPLedgerAPIServiceServerMockRecorder) GetLedgerData(arg0, arg1 interface{}) *gomock.Call {
 | 
			
		||||
	mr.mock.ctrl.T.Helper()
 | 
			
		||||
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLedgerData", reflect.TypeOf((*MockXRPLedgerAPIServiceServer)(nil).GetLedgerData), arg0, arg1)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetLedgerDiff mocks base method.
 | 
			
		||||
func (m *MockXRPLedgerAPIServiceServer) GetLedgerDiff(arg0 context.Context, arg1 *v1.GetLedgerDiffRequest) (*v1.GetLedgerDiffResponse, error) {
 | 
			
		||||
	m.ctrl.T.Helper()
 | 
			
		||||
	ret := m.ctrl.Call(m, "GetLedgerDiff", arg0, arg1)
 | 
			
		||||
	ret0, _ := ret[0].(*v1.GetLedgerDiffResponse)
 | 
			
		||||
	ret1, _ := ret[1].(error)
 | 
			
		||||
	return ret0, ret1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetLedgerDiff indicates an expected call of GetLedgerDiff.
 | 
			
		||||
func (mr *MockXRPLedgerAPIServiceServerMockRecorder) GetLedgerDiff(arg0, arg1 interface{}) *gomock.Call {
 | 
			
		||||
	mr.mock.ctrl.T.Helper()
 | 
			
		||||
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLedgerDiff", reflect.TypeOf((*MockXRPLedgerAPIServiceServer)(nil).GetLedgerDiff), arg0, arg1)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetLedgerEntry mocks base method.
 | 
			
		||||
func (m *MockXRPLedgerAPIServiceServer) GetLedgerEntry(arg0 context.Context, arg1 *v1.GetLedgerEntryRequest) (*v1.GetLedgerEntryResponse, error) {
 | 
			
		||||
	m.ctrl.T.Helper()
 | 
			
		||||
	ret := m.ctrl.Call(m, "GetLedgerEntry", arg0, arg1)
 | 
			
		||||
	ret0, _ := ret[0].(*v1.GetLedgerEntryResponse)
 | 
			
		||||
	ret1, _ := ret[1].(error)
 | 
			
		||||
	return ret0, ret1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetLedgerEntry indicates an expected call of GetLedgerEntry.
 | 
			
		||||
func (mr *MockXRPLedgerAPIServiceServerMockRecorder) GetLedgerEntry(arg0, arg1 interface{}) *gomock.Call {
 | 
			
		||||
	mr.mock.ctrl.T.Helper()
 | 
			
		||||
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLedgerEntry", reflect.TypeOf((*MockXRPLedgerAPIServiceServer)(nil).GetLedgerEntry), arg0, arg1)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// mustEmbedUnimplementedXRPLedgerAPIServiceServer mocks base method.
 | 
			
		||||
func (m *MockXRPLedgerAPIServiceServer) mustEmbedUnimplementedXRPLedgerAPIServiceServer() {
 | 
			
		||||
	m.ctrl.T.Helper()
 | 
			
		||||
	m.ctrl.Call(m, "mustEmbedUnimplementedXRPLedgerAPIServiceServer")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// mustEmbedUnimplementedXRPLedgerAPIServiceServer indicates an expected call of mustEmbedUnimplementedXRPLedgerAPIServiceServer.
 | 
			
		||||
func (mr *MockXRPLedgerAPIServiceServerMockRecorder) mustEmbedUnimplementedXRPLedgerAPIServiceServer() *gomock.Call {
 | 
			
		||||
	mr.mock.ctrl.T.Helper()
 | 
			
		||||
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "mustEmbedUnimplementedXRPLedgerAPIServiceServer", reflect.TypeOf((*MockXRPLedgerAPIServiceServer)(nil).mustEmbedUnimplementedXRPLedgerAPIServiceServer))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MockUnsafeXRPLedgerAPIServiceServer is a mock of UnsafeXRPLedgerAPIServiceServer interface.
 | 
			
		||||
type MockUnsafeXRPLedgerAPIServiceServer struct {
 | 
			
		||||
	ctrl     *gomock.Controller
 | 
			
		||||
	recorder *MockUnsafeXRPLedgerAPIServiceServerMockRecorder
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MockUnsafeXRPLedgerAPIServiceServerMockRecorder is the mock recorder for MockUnsafeXRPLedgerAPIServiceServer.
 | 
			
		||||
type MockUnsafeXRPLedgerAPIServiceServerMockRecorder struct {
 | 
			
		||||
	mock *MockUnsafeXRPLedgerAPIServiceServer
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewMockUnsafeXRPLedgerAPIServiceServer creates a new mock instance.
 | 
			
		||||
func NewMockUnsafeXRPLedgerAPIServiceServer(ctrl *gomock.Controller) *MockUnsafeXRPLedgerAPIServiceServer {
 | 
			
		||||
	mock := &MockUnsafeXRPLedgerAPIServiceServer{ctrl: ctrl}
 | 
			
		||||
	mock.recorder = &MockUnsafeXRPLedgerAPIServiceServerMockRecorder{mock}
 | 
			
		||||
	return mock
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// EXPECT returns an object that allows the caller to indicate expected use.
 | 
			
		||||
func (m *MockUnsafeXRPLedgerAPIServiceServer) EXPECT() *MockUnsafeXRPLedgerAPIServiceServerMockRecorder {
 | 
			
		||||
	return m.recorder
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// mustEmbedUnimplementedXRPLedgerAPIServiceServer mocks base method.
 | 
			
		||||
func (m *MockUnsafeXRPLedgerAPIServiceServer) mustEmbedUnimplementedXRPLedgerAPIServiceServer() {
 | 
			
		||||
	m.ctrl.T.Helper()
 | 
			
		||||
	m.ctrl.Call(m, "mustEmbedUnimplementedXRPLedgerAPIServiceServer")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// mustEmbedUnimplementedXRPLedgerAPIServiceServer indicates an expected call of mustEmbedUnimplementedXRPLedgerAPIServiceServer.
 | 
			
		||||
func (mr *MockUnsafeXRPLedgerAPIServiceServerMockRecorder) mustEmbedUnimplementedXRPLedgerAPIServiceServer() *gomock.Call {
 | 
			
		||||
	mr.mock.ctrl.T.Helper()
 | 
			
		||||
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "mustEmbedUnimplementedXRPLedgerAPIServiceServer", reflect.TypeOf((*MockUnsafeXRPLedgerAPIServiceServer)(nil).mustEmbedUnimplementedXRPLedgerAPIServiceServer))
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										23
									
								
								tools/snapshot/snapshot.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								tools/snapshot/snapshot.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,23 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"log"
 | 
			
		||||
 | 
			
		||||
	"xrplf/clio/clio_snapshot/internal/export"
 | 
			
		||||
	"xrplf/clio/clio_snapshot/internal/parse_args"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	args, err := parse_args.Parse()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if args.ExportMode == "full" {
 | 
			
		||||
		export.ExportFromFullLedger(args.GrpcServer, args.StartSeq, args.EndSeq, args.Path)
 | 
			
		||||
 | 
			
		||||
	} else if args.ExportMode == "delta" {
 | 
			
		||||
		export.ExportFromDeltaLedger(args.GrpcServer, args.StartSeq, args.EndSeq, args.Path)
 | 
			
		||||
	}
 | 
			
		||||
	//TODO: Implement server mode
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user