diff --git a/chainconfigmgr/chain_config_contract.go b/chainconfigmgr/chain_config_contract.go index 66f4e18cef54b2ff4516006e16bf4746ff1506ab..8e7bf28d7b9bb424a0b308cdd893bb9b2547625b 100644 --- a/chainconfigmgr/chain_config_contract.go +++ b/chainconfigmgr/chain_config_contract.go @@ -28,6 +28,7 @@ import ( const ( paramNameOrgId = "org_id" paramNameRoot = "root" + paramNameMasterKey = "master_key" paramNameNodeIds = "node_ids" paramNameNodeId = "node_id" paramNameNewNodeId = "new_node_id" @@ -105,6 +106,15 @@ func registerChainConfigContractMethods(log protocol.Logger) map[string]common.C methodMap[syscontract.ChainConfigFunction_TRUST_ROOT_DELETE.String()] = wrapEventResult( trustRootsRuntime.TrustRootDelete) + // [ibc_master_key] + masterKeysRuntime := &ChainIBCMasterKeysRuntime{log: log} + methodMap[syscontract.ChainConfigFunction_IBC_MASTER_KEY_ADD.String()] = wrapEventResult( + masterKeysRuntime.MasterKeyAdd) + methodMap[syscontract.ChainConfigFunction_IBC_MASTER_KEY_UPDATE.String()] = wrapEventResult( + masterKeysRuntime.MasterKeyUpdate) + methodMap[syscontract.ChainConfigFunction_IBC_MASTER_KEY_DELETE.String()] = wrapEventResult( + masterKeysRuntime.MasterKeyDelete) + // [trust_Member] trustMembersRuntime := &ChainTrustMembersRuntime{log: log} methodMap[syscontract.ChainConfigFunction_TRUST_MEMBER_ADD.String()] = wrapEventResult( diff --git a/chainconfigmgr/chain_config_ibc_master_key.go b/chainconfigmgr/chain_config_ibc_master_key.go new file mode 100644 index 0000000000000000000000000000000000000000..d929f09f036b9492ea54e11ba20d3a942cd8cb75 --- /dev/null +++ b/chainconfigmgr/chain_config_ibc_master_key.go @@ -0,0 +1,167 @@ +/* +Copyright (C) Beijing Advanced Innovation Center for Future Blockchain and Privacy Computing (未来区块链与隐 +私计算⾼精尖创新中⼼). All rights reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package chainconfigmgr + +import ( + "fmt" + "strings" + + configPb "chainmaker.org/chainmaker/pb-go/v2/config" + "chainmaker.org/chainmaker/protocol/v2" + "chainmaker.org/chainmaker/utils/v2" + "chainmaker.org/chainmaker/vm-native/v2/common" +) + +// ChainIBCMasterKeysRuntime chain ibc master key manager +type ChainIBCMasterKeysRuntime struct { + log protocol.Logger +} + +// MasterKeyAdd add master key +func (r *ChainIBCMasterKeysRuntime) MasterKeyAdd(txSimContext protocol.TxSimContext, params map[string][]byte) ( + result []byte, err error) { + // [start] + chainConfig, err := common.GetChainConfig(txSimContext) + if err != nil { + r.log.Error(err) + return nil, err + } + + orgId := string(params[paramNameOrgId]) + masterKeysStr := string(params[paramNameMasterKey]) + if utils.IsAnyBlank(orgId, masterKeysStr) { + err = fmt.Errorf("%s, add master key require param [%s, %s] not found", + common.ErrParams.Error(), paramNameOrgId, paramNameMasterKey) + r.log.Error(err) + return nil, err + } + masterKeys := strings.Split(masterKeysStr, ",") + + existOrgId := false + for i := 0; i < len(chainConfig.IbcMasterKeys); i++ { + if chainConfig.IbcMasterKeys[i].OrgId == orgId { + existOrgId = true + chainConfig.IbcMasterKeys[i].MasterKeys = append(chainConfig.IbcMasterKeys[i].MasterKeys, masterKeys...) + } + } + + if !existOrgId { + masterKey := &configPb.IBCMasterKeyConfig{OrgId: orgId, MasterKeys: masterKeys} + chainConfig.IbcMasterKeys = append(chainConfig.IbcMasterKeys, masterKey) + } + + result, err = SetChainConfig(txSimContext, chainConfig) + if err != nil { + r.log.Errorf("master key add fail, %s, orgId[%s] cert[%s]", err.Error(), orgId, masterKeysStr) + } else { + r.log.Infof("master key add success. orgId[%s] cert[%s]", orgId, masterKeysStr) + } + return result, err +} + +// MasterKeyUpdate update the masterKey +func (r *ChainIBCMasterKeysRuntime) MasterKeyUpdate(txSimContext protocol.TxSimContext, params map[string][]byte) ( + result []byte, err error) { + // [start] + chainConfig, err := common.GetChainConfig(txSimContext) + if err != nil { + r.log.Error(err) + return nil, err + } + + orgId := string(params[paramNameOrgId]) + masterKeysStr := string(params[paramNameMasterKey]) + if utils.IsAnyBlank(orgId, masterKeysStr) { + err = fmt.Errorf("%s, add master key require param [%s, %s] not found", + common.ErrParams.Error(), paramNameOrgId, paramNameMasterKey) + r.log.Error(err) + return nil, err + } + + // verify has consensus nodes + for _, node := range chainConfig.Consensus.Nodes { + if orgId == node.OrgId { + err = fmt.Errorf("update master key failed, you must delete all consensus nodes under the organization first") + r.log.Error(err) + return nil, err + } + } + + masterKeys := strings.Split(masterKeysStr, ",") + + ibcMasterKeys := chainConfig.IbcMasterKeys + for i, masterKey := range ibcMasterKeys { + if orgId == masterKey.OrgId { + ibcMasterKeys[i] = &configPb.IBCMasterKeyConfig{OrgId: orgId, MasterKeys: masterKeys} + result, err = SetChainConfig(txSimContext, chainConfig) + if err != nil { + r.log.Errorf("master key update fail, %s, orgId[%s] masterKey[%s]", err.Error(), orgId, masterKeysStr) + } else { + r.log.Infof("trust root update success. orgId[%s] masterKey[%s]", orgId, masterKeysStr) + } + return result, err + } + } + + err = fmt.Errorf("%s can not found orgId[%s]", common.ErrParams.Error(), orgId) + r.log.Error(err) + return nil, err +} + +// MasterKeyDelete delete the org all master key +func (r *ChainIBCMasterKeysRuntime) MasterKeyDelete(txSimContext protocol.TxSimContext, params map[string][]byte) ( + result []byte, err error) { + // [start] + chainConfig, err := common.GetChainConfig(txSimContext) + if err != nil { + r.log.Error(err) + return nil, err + } + + orgId := string(params[paramNameOrgId]) + if utils.IsAnyBlank(orgId) { + err = fmt.Errorf("delete master key failed, require param [%s], but not found", paramNameOrgId) + r.log.Error(err) + return nil, err + } + + index := -1 + masterKeys := chainConfig.IbcMasterKeys + for i, root := range masterKeys { + if orgId == root.OrgId { + index = i + break + } + } + + if index == -1 { + err = fmt.Errorf("delete master key failed failed, param [%s] not found from MasterKey", orgId) + r.log.Error(err) + return nil, err + } + + masterKeys = append(masterKeys[:index], masterKeys[index+1:]...) + + // verify has consensus nodes + for _, node := range chainConfig.Consensus.Nodes { + if orgId == node.OrgId { + err = fmt.Errorf("update master key failed, you must delete all consensus nodes under the organization first") + r.log.Error(err) + return nil, err + } + } + + chainConfig.IbcMasterKeys = masterKeys + result, err = SetChainConfig(txSimContext, chainConfig) + if err != nil { + r.log.Errorf("master key delete fail, %s, orgId[%s] ", err.Error(), orgId) + } else { + r.log.Infof("master key delete success. orgId[%s]", orgId) + } + return result, err +} diff --git a/go.mod b/go.mod index 3349c3f6e6846588964059c91f6998c907832b33..e81725fb7e943f92ce3f6b26d60664df5300b647 100644 --- a/go.mod +++ b/go.mod @@ -3,11 +3,11 @@ module chainmaker.org/chainmaker/vm-native/v2 go 1.16 require ( - chainmaker.org/chainmaker/chainconf/v2 v2.3.0 - chainmaker.org/chainmaker/common/v2 v2.3.0 + chainmaker.org/chainmaker/chainconf/v2 v2.3.1-0.20230105074858-60477b35fa67 + chainmaker.org/chainmaker/common/v2 v2.3.1-0.20230105071251-ec7374344cef chainmaker.org/chainmaker/localconf/v2 v2.3.0 - chainmaker.org/chainmaker/pb-go/v2 v2.3.0 - chainmaker.org/chainmaker/protocol/v2 v2.3.0 + chainmaker.org/chainmaker/pb-go/v2 v2.3.2-0.20230105064828-6c82495a6a5e + chainmaker.org/chainmaker/protocol/v2 v2.3.2-0.20230105072246-0590ca20f44e chainmaker.org/chainmaker/utils/v2 v2.3.1 github.com/gogo/protobuf v1.3.2 github.com/golang/mock v1.6.0 diff --git a/go.sum b/go.sum index 81182247f51b2415e7be51035911fe518266de51..29d584496905528372c8badb4a115b3cbcaac570 100644 --- a/go.sum +++ b/go.sum @@ -1,15 +1,19 @@ -chainmaker.org/chainmaker/chainconf/v2 v2.3.0 h1:AvgRmasWZqHCqkOI5yBU2YsVFbLeI/5Mk9jCJvWyQLQ= -chainmaker.org/chainmaker/chainconf/v2 v2.3.0/go.mod h1:JXISUHzKPGMcRh59OupxMNj2pB9Xih/wao8GkxLntPw= -chainmaker.org/chainmaker/common/v2 v2.3.0 h1:ZjJrDnHGQUSdCI7154zAiBAiLlmLkd+DroQ0NUpRd5o= +chainmaker.org/chainmaker/chainconf/v2 v2.3.1-0.20230105074858-60477b35fa67 h1:kw4ax0zvtEb11p9Wq7TKzho9GzCrAHa7nl/d/fp7Msc= +chainmaker.org/chainmaker/chainconf/v2 v2.3.1-0.20230105074858-60477b35fa67/go.mod h1:UTC4qCv8jrr8pyPYiDKOEmq/Z2rzFEpYqnnj1M8AsFQ= chainmaker.org/chainmaker/common/v2 v2.3.0/go.mod h1:LV6bEVvqWBa6NY/QyNY9CDIdah44Hpd/aEqWkG5rXws= +chainmaker.org/chainmaker/common/v2 v2.3.1-0.20230105071251-ec7374344cef h1:LqG6Z5a8TsiwF/rN1RmIK1ZVRtf7goTRWywR0VCWHhc= +chainmaker.org/chainmaker/common/v2 v2.3.1-0.20230105071251-ec7374344cef/go.mod h1:qP/4Kwd1k3X/tLU+ae85cQpOytGxjv3lZdF1fJu+XY8= chainmaker.org/chainmaker/localconf/v2 v2.3.0 h1:xkwW31eFgAt4T2+ddD6j+kBKotM0bbI+svbHRW8pd4o= chainmaker.org/chainmaker/localconf/v2 v2.3.0/go.mod h1:FsrIi0ts5Ba9MmRhg+j+dLAB9oGyVTQsPgjSA39Ql+k= chainmaker.org/chainmaker/logger/v2 v2.3.0 h1:SuBA48m+OQ5HChX34j6eXk4o0uhXHi8Jfkrjf90U7rQ= chainmaker.org/chainmaker/logger/v2 v2.3.0/go.mod h1:kmHXCIBl9833c0UO+G4GKAifbhqhNdiL4f5dDsR+Vks= -chainmaker.org/chainmaker/pb-go/v2 v2.3.0 h1:GcFY14KFGDnDlWw07VWL9Ew5XqUBkbcjm7IHtMgmF2s= +chainmaker.org/chainmaker/pb-go/v2 v2.0.0-20230105064828-6c82495a6a5e/go.mod h1:MB2+suualBWOKvd6FRQD/XcZzlav7APiSa7uzdDLkY8= chainmaker.org/chainmaker/pb-go/v2 v2.3.0/go.mod h1:MB2+suualBWOKvd6FRQD/XcZzlav7APiSa7uzdDLkY8= -chainmaker.org/chainmaker/protocol/v2 v2.3.0 h1:c/Mgq6Fdx0A6rRhgX1H9uyYpYwExM332LsSutmkiGP4= +chainmaker.org/chainmaker/pb-go/v2 v2.3.2-0.20230105064828-6c82495a6a5e h1:OR9QZxWn8FQ1HPspvbyXWvpY3vQu3EprVRXnmukhQJE= +chainmaker.org/chainmaker/pb-go/v2 v2.3.2-0.20230105064828-6c82495a6a5e/go.mod h1:MB2+suualBWOKvd6FRQD/XcZzlav7APiSa7uzdDLkY8= chainmaker.org/chainmaker/protocol/v2 v2.3.0/go.mod h1:l3EfuaCdGG1FKcCItDbGeIJ4YmYnMjSUIoXQ5GElARY= +chainmaker.org/chainmaker/protocol/v2 v2.3.2-0.20230105072246-0590ca20f44e h1:m1mJ1zujfkCYRmwy995picix8dTpj3RCi4jaSrXV5Mo= +chainmaker.org/chainmaker/protocol/v2 v2.3.2-0.20230105072246-0590ca20f44e/go.mod h1:tNsV1lKmn9CKGCbNwsWuw7GNeJRBuOsedXYJ39tatuY= chainmaker.org/chainmaker/utils/v2 v2.3.0/go.mod h1:NXZXc0fVrRCpWqrVyFP/WyobSeT3gz2zmL4uA0Lqvoo= chainmaker.org/chainmaker/utils/v2 v2.3.1 h1:+H7hSShIMPQGrIHYC6hkIVBnn5N7+jtGG0xyAQDi4ZY= chainmaker.org/chainmaker/utils/v2 v2.3.1/go.mod h1:NXZXc0fVrRCpWqrVyFP/WyobSeT3gz2zmL4uA0Lqvoo= @@ -231,6 +235,8 @@ github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZi github.com/elazarl/go-bindata-assetfs v1.0.0/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4= github.com/elgs/gojq v0.0.0-20201120033525-b5293fef2759/go.mod h1:rQELVIqRXpraeUryHOBadz99ePvEVQmTVpGr8M9QQ4Q= github.com/elgs/gosplitargs v0.0.0-20161028071935-a491c5eeb3c8/go.mod h1:o4DgpccPNAQAlPSxo7I4L/LWNh2oyr/BBGSynrLTmZM= +github.com/emmansun/gmsm v0.14.1 h1:Qq7uxLe1DoCs1DlX9bMdX3pwdwhtw/DbDZJgysvB1CU= +github.com/emmansun/gmsm v0.14.1/go.mod h1:wf9+tyGCA1TRY5IBtaE8uZ4tnaL/ZkmRWMavUdzq0Iw= github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -816,6 +822,7 @@ github.com/pingcap/tipb v0.0.0-20200417094153-7316d94df1ee/go.mod h1:RtkHW8WbcNx github.com/pingcap/tipb v0.0.0-20200522051215-f31a15d98fce/go.mod h1:RtkHW8WbcNxj8lsbzjaILci01CtYnYbIkQhjyZWrWVI= github.com/pingcap/tipb v0.0.0-20210425040103-dc47a87b52aa h1:lPZ5NmNel7RKlX4D9sU045y04knCMEaBTUrPAxb45os= github.com/pingcap/tipb v0.0.0-20210425040103-dc47a87b52aa/go.mod h1:nsEhnMokcn7MRqd2J60yxpn/ac3ZH8A6GOJ9NslabUo= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -872,8 +879,9 @@ github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qq github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= +github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= @@ -1109,8 +1117,9 @@ golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20220214200702-86341886e292 h1:f+lwQ+GtmgoY+A2YaQxlSOnDjXcQ7ZRLWOHbC6HtRqE= golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 h1:Y/gsMcFOcR+6S6f3YeMKl5g+dZMEWqcz5Czj/GWYbkM= +golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -1317,8 +1326,9 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220222200937-f2425489ef4c h1:sSIdNI2Dd6vGv47bKc/xArpfxVmEz2+3j0E6I484xC4= golang.org/x/sys v0.0.0-20220222200937-f2425489ef4c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 h1:v6hYoSR9T5oet+pMXwUWkbiVqx/63mlHjefrHmxwfeY= +golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -1562,8 +1572,9 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/alecthomas/gometalinter.v2 v2.0.12/go.mod h1:NDRytsqEZyolNuAgTzJkZMkSQM7FIKyzVzGhjB/qfYo= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/alecthomas/kingpin.v3-unstable v3.0.0-20180810215634-df19058c872c/go.mod h1:3HH7i1SgMqlzxCcBmUHW657sD4Kvv9sC3HpL3YukzwA=