Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: ADR-005: Support multisig chain link #708

Merged
merged 46 commits into from
Jan 14, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
b476e11
Apply SignatureData in Proof
dadamu Dec 22, 2021
11e307d
Fix types tests
dadamu Dec 22, 2021
45b6567
Add signature testutil
dadamu Dec 22, 2021
b6df42d
changed how signature data should be provided inside the proof
RiccardoM Jan 5, 2022
2fc266d
Register interface
dadamu Jan 6, 2022
cdeb8a5
Fix panic in test
dadamu Jan 6, 2022
bef25df
Remove signature nil checking since it causes panic
dadamu Jan 6, 2022
73e6016
Add back gogoproto.equal
dadamu Jan 6, 2022
73430a8
Update profiles types unit tests on multisig chainlink
dadamu Jan 7, 2022
e05f47a
Update keeper unit tests
dadamu Jan 7, 2022
4772a51
Update cli unit tests
dadamu Jan 7, 2022
6b35fe3
Update cli and v210 migration test
dadamu Jan 10, 2022
a9de5f0
Fix proof verify unpack issue
dadamu Jan 10, 2022
3a84c6c
Fix lint
dadamu Jan 10, 2022
7752995
Add invalid signature test for Proof
dadamu Jan 10, 2022
144fac9
Move SignatureDataFromCosmosSignatureData to types
dadamu Jan 10, 2022
c82287e
Build multisig chain link json cli
dadamu Jan 10, 2022
1bc9de5
Add unit test for chain link from multisign cli
dadamu Jan 10, 2022
9c6981e
Remove unused config
dadamu Jan 10, 2022
7d1bf62
Fix lint
dadamu Jan 10, 2022
61753db
Fix SignatureDataFromCosmosSignatureData to handle error properly
dadamu Jan 10, 2022
37cb980
Update create chain link json
dadamu Jan 10, 2022
1e919a3
Update app/desmos/cmd/chainlink/create_json.go
dadamu Jan 10, 2022
d5c3dc9
Update app/desmos/cmd/chainlink/types/getter.go
dadamu Jan 10, 2022
25eeaf2
Apply suggestions create chain link json cmd
dadamu Jan 11, 2022
58675f3
Add doc for testutil
dadamu Jan 11, 2022
69716b5
Merge branch 'paul/adr-005-multisig-chainlink-impl' of github.com:des…
dadamu Jan 11, 2022
86b8b2c
Merge branch 'master' of github.com:desmos-labs/desmos into paul/adr-…
dadamu Jan 11, 2022
b04b997
Fix doc in the getter
dadamu Jan 12, 2022
fc4dd7d
Update changeset
dadamu Jan 12, 2022
b017cca
Revert generateChainLinkJSON
dadamu Jan 12, 2022
bd5bd67
Update app/desmos/cmd/chainlink/create_json.go
dadamu Jan 12, 2022
2f0dae5
Fix the comment in cli test
dadamu Jan 12, 2022
65417f4
Merge branch 'paul/adr-005-multisig-chainlink-impl' of github.com:des…
dadamu Jan 12, 2022
a364e24
Add test to check if signature is nil
dadamu Jan 12, 2022
31854ac
Remove unused function
dadamu Jan 12, 2022
60deaa2
Add more tests for multisig proof
dadamu Jan 12, 2022
41c7496
improved the overall code organization
RiccardoM Jan 12, 2022
40637f3
Merge branch 'paul/adr-005-multisig-chainlink-impl' of github.com:des…
RiccardoM Jan 12, 2022
1769abb
improved the overall code organization
RiccardoM Jan 12, 2022
f198b21
Fix sign mode and test builder
dadamu Jan 13, 2022
ef18023
Rerange the files
dadamu Jan 13, 2022
3f274d9
Update .changeset/entries/a492eff6c4ec45c98cb555b14de9f27ba50da5b147f…
dadamu Jan 14, 2022
04b9181
small doc fixes
RiccardoM Jan 14, 2022
9eb1093
Merge remote-tracking branch 'origin/paul/adr-005-multisig-chainlink-…
RiccardoM Jan 14, 2022
78be05b
Merge branch 'master' into paul/adr-005-multisig-chainlink-impl
mergify[bot] Jan 14, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
type: feat
module: x/profiles
pull_request: 708
description: Added support for multisig chain links
backward_compatible: false
date: 2022-01-12T03:49:42.2638125Z
6 changes: 6 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,12 @@ proto-update-deps:
@mkdir -p $(IBC_TYPES)/core/client/v1
@curl -sSL $(IBC_URL)/core/client/v1/client.proto > $(IBC_TYPES)/core/client/v1/client.proto

@mkdir -p $(COSMOS_TYPES)/tx/signing/v1beta1
@curl -sSL $(COSMOS_URL)/tx/signing/v1beta1/signing.proto > $(COSMOS_TYPES)/tx/signing/v1beta1/signing.proto

@mkdir -p $(COSMOS_TYPES)/crypto/multisig/v1beta1
@curl -sSL $(COSMOS_URL)/crypto/multisig/v1beta1/multisig.proto > $(COSMOS_TYPES)/crypto/multisig/v1beta1/multisig.proto

## Importing of tendermint protobuf definitions currently requires the
## use of `sed` in order to build properly with cosmos-sdk's proto file layout
## (which is the standard Buf.build FILE_LAYOUT)
Expand Down
15 changes: 15 additions & 0 deletions app/desmos/cmd/chainlink/builder/builder.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package builder

import (
"github.com/desmos-labs/desmos/v2/app/desmos/cmd/chainlink/types"
"github.com/desmos-labs/desmos/v2/x/profiles/client/utils"
)

// ChainLinkJSONBuilder allows to build a ChainLinkJSON instance
type ChainLinkJSONBuilder interface {
BuildChainLinkJSON(chain types.Chain) (utils.ChainLinkJSON, error)
}

// ChainLinkJSONBuilderProvider allows to provide the provider ChainLinkJSONBuilder implementation based on whether
// it should create the JSON chain link for single or
type ChainLinkJSONBuilderProvider func(isSingleAccount bool) ChainLinkJSONBuilder
99 changes: 99 additions & 0 deletions app/desmos/cmd/chainlink/builder/multi/builder.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package multi

import (
"encoding/hex"
"fmt"
"io/ioutil"

sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/tx/signing"
authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing"

"github.com/desmos-labs/desmos/v2/app"
"github.com/desmos-labs/desmos/v2/app/desmos/cmd/chainlink/getter"
"github.com/desmos-labs/desmos/v2/app/desmos/cmd/chainlink/types"
"github.com/desmos-labs/desmos/v2/x/profiles/client/utils"
profilestypes "github.com/desmos-labs/desmos/v2/x/profiles/types"
)

// AccountChainLinkJSONBuilder implements the ChainLinkJSONBuilder for multi signature accounts
type AccountChainLinkJSONBuilder struct {
getter getter.MultiSignatureAccountReferenceGetter
}

// NewAccountChainLinkJSONBuilder returns a new AccountChainLinkJSONBuilder instance
func NewAccountChainLinkJSONBuilder(getter getter.MultiSignatureAccountReferenceGetter) *AccountChainLinkJSONBuilder {
return &AccountChainLinkJSONBuilder{
getter: getter,
}
}

// BuildChainLinkJSON implements ChainLinkJSONBuilder
func (b *AccountChainLinkJSONBuilder) BuildChainLinkJSON(chain types.Chain) (utils.ChainLinkJSON, error) {
txFilePath, err := b.getter.GetMultiSignedTxFilePath()
if err != nil {
return utils.ChainLinkJSON{}, err
}

signedChainID, err := b.getter.GetSignedChainID()
if err != nil {
return utils.ChainLinkJSON{}, err
}

encodingConfig := app.MakeTestEncodingConfig()
txCfg := encodingConfig.TxConfig

// Read the transaction file
bytes, err := ioutil.ReadFile(txFilePath)
if err != nil {
return utils.ChainLinkJSON{}, err
}

// Parse the transaction
parsedTx, err := txCfg.TxJSONDecoder()(bytes)
if err != nil {
return utils.ChainLinkJSON{}, err
}

// Get the sign mode
signMode := signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One thing I don't understand if it's correct or not is this line: the transaction could be signed using SIGN_MODE_DIRECT as well from what I know. Isn't this valid for multi-sig transactions as well? Or are they all signed using the SIGN_MODE_LEGACY_AMINO_JSON?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They are all signed in SIGN_MODE_LEGACY_AMINO_JSON if it is the multi-sig tx as well as the ledger tx.

Reference

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dadamu Perfect, thanks for the reference link as well! 🙏


// Wrap the transaction inside a builder to make it easier to get the signatures
txBuilder, err := txCfg.WrapTxBuilder(parsedTx)
if err != nil {
return utils.ChainLinkJSON{}, err
}

sigs, err := txBuilder.GetTx().GetSignaturesV2()
if err != nil {
return utils.ChainLinkJSON{}, err
}

// Make sure there is only one signature for the multisig account
if len(sigs) != 1 {
return utils.ChainLinkJSON{}, fmt.Errorf("invalid number of signatures")
}

// Re-create the bytes that have been signed in order to produce the signature
signingData := authsigning.SignerData{AccountNumber: 0, Sequence: 0, ChainID: signedChainID}
value, err := txCfg.SignModeHandler().GetSignBytes(signMode, signingData, parsedTx)
if err != nil {
return utils.ChainLinkJSON{}, err
}

addr, err := sdk.Bech32ifyAddressBytes(chain.Prefix, sigs[0].PubKey.Address().Bytes())
if err != nil {
return utils.ChainLinkJSON{}, err
}

sigData, err := profilestypes.SignatureDataFromCosmosSignatureData(sigs[0].Data)
if err != nil {
return utils.ChainLinkJSON{}, err
}

return utils.NewChainLinkJSON(
profilestypes.NewBech32Address(addr, chain.Prefix),
profilestypes.NewProof(sigs[0].PubKey, sigData, hex.EncodeToString(value)),
profilestypes.NewChainConfig(chain.Name),
), err
}
65 changes: 65 additions & 0 deletions app/desmos/cmd/chainlink/builder/single/builder.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package single

import (
"encoding/hex"

"github.com/cosmos/cosmos-sdk/crypto/hd"
"github.com/cosmos/cosmos-sdk/crypto/keyring"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/tx/signing"

"github.com/desmos-labs/desmos/v2/app/desmos/cmd/chainlink/getter"
"github.com/desmos-labs/desmos/v2/app/desmos/cmd/chainlink/types"
"github.com/desmos-labs/desmos/v2/x/profiles/client/utils"
profilestypes "github.com/desmos-labs/desmos/v2/x/profiles/types"
)

const (
KeyName = "desmos_chain_link_account"
)

// AccountChainLinkJSONBuilder implements the ChainLinkJSONBuilder for single signature accounts
type AccountChainLinkJSONBuilder struct {
getter getter.SingleSignatureAccountReferenceGetter
}

// NewAccountChainLinkJSONBuilder returns a new AccountChainLinkJSONBuilder instance
func NewAccountChainLinkJSONBuilder(getter getter.SingleSignatureAccountReferenceGetter) *AccountChainLinkJSONBuilder {
return &AccountChainLinkJSONBuilder{
getter: getter,
}
}

// BuildChainLinkJSON implements ChainLinkJSONBuilder
func (b *AccountChainLinkJSONBuilder) BuildChainLinkJSON(chain types.Chain) (utils.ChainLinkJSON, error) {
mnemonic, err := b.getter.GetMnemonic()
if err != nil {
return utils.ChainLinkJSON{}, err
}

// Create an in-memory keybase for signing
keyBase := keyring.NewInMemory()
_, err = keyBase.NewAccount(KeyName, mnemonic, "", chain.DerivationPath, hd.Secp256k1)
if err != nil {
return utils.ChainLinkJSON{}, err
}

// Generate the proof signing it with the key
key, _ := keyBase.Key(KeyName)
addr, _ := sdk.Bech32ifyAddressBytes(chain.Prefix, key.GetAddress())
value := []byte(addr)
sig, pubkey, err := keyBase.Sign(KeyName, value)
if err != nil {
return utils.ChainLinkJSON{}, err
}
sigData := &profilestypes.SingleSignatureData{
Mode: signing.SignMode_SIGN_MODE_DIRECT,
Signature: sig,
}

return utils.NewChainLinkJSON(
profilestypes.NewBech32Address(addr, chain.Prefix),
profilestypes.NewProof(pubkey, sigData, hex.EncodeToString(value)),
profilestypes.NewChainConfig(chain.Name),
), nil
}
71 changes: 23 additions & 48 deletions app/desmos/cmd/chainlink/create_json.go
Original file line number Diff line number Diff line change
@@ -1,25 +1,23 @@
package chainlink

import (
"encoding/hex"
"fmt"
"io/ioutil"

chainlinktypes "github.com/desmos-labs/desmos/v2/app/desmos/cmd/chainlink/types"
"github.com/desmos-labs/desmos/v2/app/desmos/cmd/chainlink/builder"
chainlinktypes "github.com/desmos-labs/desmos/v2/app/desmos/cmd/chainlink/getter"

"github.com/cosmos/cosmos-sdk/crypto/hd"
"github.com/cosmos/cosmos-sdk/crypto/keyring"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/spf13/cobra"

"github.com/desmos-labs/desmos/v2/app"
profilescliutils "github.com/desmos-labs/desmos/v2/x/profiles/client/utils"
profilestypes "github.com/desmos-labs/desmos/v2/x/profiles/types"
)

// GetCreateChainLinkJSON returns the command allowing to generate the chain link JSON
// file that is required by the link-chain command
func GetCreateChainLinkJSON(getter chainlinktypes.ChainLinkReferenceGetter) *cobra.Command {
func GetCreateChainLinkJSON(
getter chainlinktypes.ChainLinkReferenceGetter,
provider builder.ChainLinkJSONBuilderProvider,
) *cobra.Command {
return &cobra.Command{
Use: "create-chain-link-json",
Short: "Start an interactive prompt to create a new chain link JSON object",
Expand All @@ -28,11 +26,17 @@ Once you have built the JSON object using this command, you can then run the fol

desmos tx profiles link-chain [/path/to/json/file.json]

--- Single signature accounts ---
Note that this command will ask you the mnemonic that should be used to generate the private key of the address you want to link.
The mnemonic is only used temporarily and never stored anywhere.`,
The mnemonic is only used temporarily and never stored anywhere.

--- Multi signature accounts ---
If you have are using a multi-signature account, you will be required to provide the path to a signed transaction file.
That transaction must be signed as normal, except for the specified "account-number" and "sequence" values which should be both set to 0.
Providing an invalid transaction (either with an account-number or sequence not set to 0, or not signed correctly) will result in a failing linkage later on.
`,
RunE: func(cmd *cobra.Command, args []string) error {
// Get the data
mnemonic, err := getter.GetMnemonic()
isSingleSignatureAccount, err := getter.IsSingleSignatureAccount()
if err != nil {
return err
}
Expand All @@ -42,59 +46,30 @@ The mnemonic is only used temporarily and never stored anywhere.`,
return err
}

filename, err := getter.GetFilename()
chainLinkJSON, err := provider(isSingleSignatureAccount).BuildChainLinkJSON(chain)
if err != nil {
return err
}

// Build che chain link JSON
chainLinkJSON, err := generateChainLinkJSON(mnemonic, chain)
// Marshal the chain link JSON
bz, err := app.MakeTestEncodingConfig().Marshaler.MarshalJSON(&chainLinkJSON)
if err != nil {
return err
}

cdc, _ := app.MakeCodecs()
bz, err := cdc.MarshalJSON(&chainLinkJSON)
// Write the chain link JSON to a file
filename, err := getter.GetFilename()
if err != nil {
return err
}

if filename != "" {
err = ioutil.WriteFile(filename, bz, 0600)
if err != nil {
return err
}
err = ioutil.WriteFile(filename, bz, 0600)
if err != nil {
return err
}

cmd.Println(fmt.Sprintf("Chain link JSON file stored at %s", filename))

return nil
},
}
}

// generateChainLinkJSON returns build a new ChainLinkJSON intance using the provided mnemonic and chain configuration
func generateChainLinkJSON(mnemonic string, chain chainlinktypes.Chain) (profilescliutils.ChainLinkJSON, error) {
// Create an in-memory keybase for signing
keyBase := keyring.NewInMemory()
keyName := "chainlink"
_, err := keyBase.NewAccount(keyName, mnemonic, "", chain.DerivationPath, hd.Secp256k1)
if err != nil {
return profilescliutils.ChainLinkJSON{}, err
}

// Generate the proof signing it with the key
key, _ := keyBase.Key(keyName)
addr, _ := sdk.Bech32ifyAddressBytes(chain.Prefix, key.GetAddress())
value := []byte(addr)
sig, pubkey, err := keyBase.Sign(keyName, value)
if err != nil {
return profilescliutils.ChainLinkJSON{}, err
}

return profilescliutils.NewChainLinkJSON(
profilestypes.NewBech32Address(addr, chain.Prefix),
profilestypes.NewProof(pubkey, hex.EncodeToString(sig), hex.EncodeToString(value)),
profilestypes.NewChainConfig(chain.Name),
), nil
}
Loading