diff --git a/cmd/check_data.go b/cmd/check_data.go index 026285ea..535d6c71 100644 --- a/cmd/check_data.go +++ b/cmd/check_data.go @@ -25,7 +25,7 @@ import ( "github.com/coinbase/rosetta-sdk-go/fetcher" "github.com/coinbase/rosetta-sdk-go/types" "github.com/coinbase/rosetta-sdk-go/utils" - "github.com/fatih/color" + "github.com/fatih/color" "github.com/spf13/cobra" "golang.org/x/sync/errgroup" ) @@ -171,7 +171,7 @@ func runCheckDataCmd(_ *cobra.Command, _ []string) error { g, ctx := errgroup.WithContext(ctx) ctx = logger.AddMetadataMapToContext(ctx, metadataMap) - + g.Go(func() error { return dataTester.StartPeriodicLogger(ctx) }) diff --git a/cmd/root.go b/cmd/root.go index 3cca1e7b..92bb8b2e 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -375,6 +375,9 @@ default values.`, `Verify both minimum and Coinbase spec requirements`, ) rootCmd.AddCommand(checkSpecCmd) + + // Sign command + rootCmd.AddCommand(signCmd) } func initConfig() { @@ -451,7 +454,7 @@ func initConfig() { } if len(InfoMetaData) != 0 { - Config.InfoMetaData = InfoMetaData + Config.InfoMetaData = InfoMetaData } } diff --git a/cmd/sign.go b/cmd/sign.go new file mode 100644 index 00000000..4bb7cd85 --- /dev/null +++ b/cmd/sign.go @@ -0,0 +1,72 @@ +// Copyright 2023 Coinbase, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package cmd + +import ( + "encoding/hex" + "errors" + "fmt" + + "github.com/coinbase/rosetta-sdk-go/keys" + "github.com/fatih/color" + "github.com/spf13/cobra" +) + +var ( + signCmd = &cobra.Command{ + Use: "sign", + Short: "Sign an unsigned payload with given private key", + Long: `Sign an unsigned payload with given private key + It supports Keypair specified by https://github.com/coinbase/rosetta-specifications`, + RunE: runSignCmd, + } +) + +func runSignCmd(_ *cobra.Command, _ []string) error { + if Config.Sign == nil { + return errors.New("sign configuration is missing") + } + + keyPair, err := keys.ImportPrivateKey(Config.Sign.PrivateKey, Config.Sign.PubKey.CurveType) + if err != nil { + fmt.Println(fmt.Errorf("unable to import private keys %#v", err)) + return err + } + + err = keyPair.IsValid() + if err != nil { + fmt.Println(fmt.Errorf("keypair invalid with err %#v", err)) + return err + } + + signer, err := keyPair.Signer() + if err != nil { + fmt.Println(fmt.Errorf("signer invalid with err %#v", err)) + return err + } + + signingPayload := Config.Sign.SigningPayload + signatureType := Config.Sign.SigningPayload.SignatureType + + sign, err := signer.Sign(signingPayload, signatureType) + if err != nil { + fmt.Println(fmt.Errorf("unable to sign with err %#v", err)) + return err + } + + hexSig := hex.EncodeToString(sign.Bytes) + color.Blue(hexSig) + return nil +} diff --git a/cmd/utils_asserter_configuration_test.go b/cmd/utils_asserter_configuration_test.go index ce6f3cab..d7844e90 100644 --- a/cmd/utils_asserter_configuration_test.go +++ b/cmd/utils_asserter_configuration_test.go @@ -50,18 +50,18 @@ var ( allowedErrors = []*types.Error{ { - Code: 4, - Message: "Block not found", + Code: 4, + Message: "Block not found", Retriable: false, }, { - Code: 0, - Message: "Endpoint not implemented", + Code: 0, + Message: "Endpoint not implemented", Retriable: false, }, { - Code: 3, - Message: "Bitcoind error", + Code: 3, + Message: "Bitcoind error", Retriable: false, }, } @@ -93,18 +93,18 @@ func TestSortArrayFields(t *testing.T) { }, clientConfiguration.AllowedOperationStatuses) assert.Equal([]*types.Error{ { - Code: 0, - Message: "Endpoint not implemented", + Code: 0, + Message: "Endpoint not implemented", Retriable: false, }, { - Code: 3, - Message: "Bitcoind error", + Code: 3, + Message: "Bitcoind error", Retriable: false, }, { - Code: 4, - Message: "Block not found", + Code: 4, + Message: "Block not found", Retriable: false, }, }, clientConfiguration.AllowedErrors) diff --git a/cmd/validate_asserter_config_test.go b/cmd/validate_asserter_config_test.go index e48bf9c8..2a646056 100644 --- a/cmd/validate_asserter_config_test.go +++ b/cmd/validate_asserter_config_test.go @@ -17,8 +17,8 @@ package cmd import ( "github.com/coinbase/rosetta-sdk-go/asserter" "github.com/coinbase/rosetta-sdk-go/types" - "testing" "github.com/stretchr/testify/assert" + "testing" ) func TestMatch(t *testing.T) { @@ -104,10 +104,10 @@ func generateNetworkAllowAndAsserterConfiguration() ( ) { var tsi int64 = 5 allow := &types.Allow{ - OperationStatuses: generateOperationStatuses(), - OperationTypes: generateOperationTypes(), - Errors: generateErrors(), - TimestampStartIndex: &tsi, + OperationStatuses: generateOperationStatuses(), + OperationTypes: generateOperationTypes(), + Errors: generateErrors(), + TimestampStartIndex: &tsi, } config := &asserter.Configuration{ AllowedOperationStatuses: generateOperationStatuses(), @@ -127,7 +127,7 @@ func generateOperationStatuses() []*types.OperationStatus { return []*types.OperationStatus{ { Successful: true, - Status: "status0", + Status: "status0", }, { // Successful: false @@ -139,11 +139,11 @@ func generateOperationStatuses() []*types.OperationStatus { func generateErrors() []*types.Error { return []*types.Error{ { - Code: 1, + Code: 1, Message: "message1", }, { - Code: 2, + Code: 2, Message: "message2", }, } diff --git a/configuration/types.go b/configuration/types.go index 01763a04..8ad3a8f7 100644 --- a/configuration/types.go +++ b/configuration/types.go @@ -464,7 +464,7 @@ type Configuration struct { // InfoMetaData is a string, rosetta-cli will convert it into a map[string]string // key-value are separated by ":" // different key-value pairs are separated by "," - // an example: if users want to record "instance_name" as "1234", and "blockchain_name" as "Bitcoin", + // an example: if users want to record "instance_name" as "1234", and "blockchain_name" as "Bitcoin", // this field would be "instance_name:1234,blockchain_name:Bitcoin" // if adding spaces before and after ":" and ",", it will be trimmed when building map // " instance_name : xxxx , blockchain_name : xxxx " will be recorded same as @@ -474,11 +474,17 @@ type Configuration struct { Construction *ConstructionConfiguration `json:"construction"` Data *DataConfiguration `json:"data"` Perf *CheckPerfConfiguration `json:"perf"` + Sign *SignConfiguration `json:"sign"` } -//********************// -// Check Perf configs // -//********************// +// SignConfiguration configuration for signing +type SignConfiguration struct { + PubKey *types.PublicKey `json:"pub_key"` + PrivateKey string `json:"private_key"` + SigningPayload *types.SigningPayload `json:"signing_payload"` +} + +// CheckPerfConfiguration configuration for check perf type CheckPerfConfiguration struct { // StartBlock is the starting block for running check:perf. diff --git a/examples/configuration/default.json b/examples/configuration/default.json index d2c0c4fc..97e38a82 100644 --- a/examples/configuration/default.json +++ b/examples/configuration/default.json @@ -42,5 +42,6 @@ "pruning_balance_disabled": false, "initial_balance_fetch_disabled": false }, - "perf": null + "perf": null, + "sign": null } \ No newline at end of file diff --git a/examples/configuration/sign.json b/examples/configuration/sign.json new file mode 100644 index 00000000..c79d1f87 --- /dev/null +++ b/examples/configuration/sign.json @@ -0,0 +1,13 @@ +{ + "sign": { + "pub_key": { + "curve_type": "secp256k1", + "hex_bytes": "03c7e625aa08cad8f257d9ee2b9b7a0214f19f981afd5b498c728ad7ed6c0c3df6" + }, + "private_key": "", + "signing_payload": { + "hex_bytes": "370e74254e8cbaa343af3564901456082ec7af967e45ff24ba061233b1a1b04f", + "signature_type": "ecdsa" + } + } +} \ No newline at end of file diff --git a/pkg/logger/logger.go b/pkg/logger/logger.go index 5ade4f34..f18d34fe 100644 --- a/pkg/logger/logger.go +++ b/pkg/logger/logger.go @@ -102,7 +102,7 @@ func NewLogger( logReconciliation bool, checkType CheckType, network *types.NetworkIdentifier, - logMetadataMap map[string]string, + logMetadataMap map[string]string, fields ...zap.Field, ) (*Logger, error) { zapLogger, err := buildZapLogger(checkType, network, fields...) @@ -217,7 +217,7 @@ func (l *Logger) LogConstructionStatus( } statsMessage = AddMetadata(statsMessage, l.logMetadataMap) - + l.lastStatsMessage = statsMessage color.Cyan(statsMessage) } @@ -487,7 +487,7 @@ func (l *Logger) ReconcileSuccessStream( ) reconciliationSuccessString = AddMetadata(reconciliationSuccessString, l.logMetadataMap) color.Cyan(reconciliationSuccessString) - + _, err = f.WriteString(reconciliationSuccessString) if err != nil { err = fmt.Errorf("failed to write reconciliation success string %s: %w", reconciliationSuccessString, err) @@ -601,14 +601,14 @@ func (l *Logger) Fatal(msg string, fields ...zap.Field) { } // return a string of metadata -func (l *Logger) GetMetadata() string{ +func (l *Logger) GetMetadata() string { metadatMap := l.logMetadataMap metadata := ConvertMapToString(metadatMap) return metadata } // return a map of metadatMap -func (l *Logger) GetMetadataMap() map[string]string{ +func (l *Logger) GetMetadataMap() map[string]string { metadatMap := l.logMetadataMap return metadatMap } @@ -683,7 +683,7 @@ func ConvertStringToMap(metadata string) map[string]string { pairs := strings.Split(metadata, ",") for _, pair := range pairs { kv := strings.Split(pair, ":") - if(len(kv) != 2) { + if len(kv) != 2 { log := fmt.Sprintf("the %s from %s could be transfer to key value pair", pair, metadata) color.Yellow(log) } else { @@ -694,7 +694,7 @@ func ConvertStringToMap(metadata string) map[string]string { } // add requesrUUID to metadataMap -func AddRequestUUIDToMap(metadataMap map[string]string, requestUUID string) map[string]string{ +func AddRequestUUIDToMap(metadataMap map[string]string, requestUUID string) map[string]string { if len(requestUUID) > 0 { metadataMap["RequestID"] = requestUUID } @@ -711,5 +711,5 @@ func ConvertMapToString(metadataMap map[string]string) string { } } } - return metadata; + return metadata } diff --git a/pkg/processor/balance_storage_helper.go b/pkg/processor/balance_storage_helper.go index 6f929b47..704ec6a9 100644 --- a/pkg/processor/balance_storage_helper.go +++ b/pkg/processor/balance_storage_helper.go @@ -65,7 +65,7 @@ func NewBalanceStorageHelper( // Pre-process exemptAccounts on initialization // to provide fast lookup while syncing. for _, account := range exemptAccounts { - // if users do not specify Currency, we add the address + // if users do not specify Currency, we add the address // by this, all the Currencies in this address will be skipped if account.Currency == nil { exemptMap[account.Account.Address] = struct{}{} @@ -150,12 +150,12 @@ func (h *BalanceStorageHelper) ExemptFunc() parser.ExemptOperation { return true } } - // if exemptAccounts have the Account address means all the + // if exemptAccounts have the Account address means all the // currencies in this Account address need to be skipped _, existsAddress := h.exemptAccounts[op.Account.Address] if existsAddress { return existsAddress - } + } thisAcct := types.Hash(&types.AccountCurrency{ Account: op.Account, diff --git a/pkg/tester/data.go b/pkg/tester/data.go index 0487fa62..68afd896 100644 --- a/pkg/tester/data.go +++ b/pkg/tester/data.go @@ -78,7 +78,7 @@ const ( //MaxTableSize unit is MB MaxValueLogFileSize = int64(2048) - + // empty requestUUID EmptyRequestUUID = "" ) @@ -1106,7 +1106,7 @@ func (t *DataTester) HandleErr(err error, sigListeners *[]context.CancelFunc) er } return t.FindMissingOps( - ctx, + ctx, err, sigListeners, )