diff --git a/x/multisig/alias.go b/x/multisig/alias.go index 479110a..bea4e82 100755 --- a/x/multisig/alias.go +++ b/x/multisig/alias.go @@ -11,19 +11,24 @@ const ( ) var ( - NewMsgCreateWallet = types.NewMsgCreateWallet - NewMultiSigWallet = types.NewMultiSigWallet - NewMsgCreateTransaction = types.NewMsgCreateTransaction - NewTransaction = types.NewTransaction - ModuleCdc = types.ModuleCdc - RegisterCodec = types.RegisterCodec + NewMsgCreateWallet = types.NewMsgCreateWallet + NewMultiSigWallet = types.NewMultiSigWallet + NewMsgCreateTransaction = types.NewMsgCreateTransaction + NewMsgSignTransaction = types.NewMsgSignTransaction + NewMsgCompleteTransaction = types.NewMsgCompleteTransaction + NewTransaction = types.NewTransaction + ModuleCdc = types.ModuleCdc + RegisterCodec = types.RegisterCodec ) type ( - MsgCreateWallet = types.MsgCreateWallet - MsgCreateTransaction = types.MsgCreateTransaction - QueryResResolve = types.QueryResResolve - QueryResNames = types.QueryResNames - Transaction = types.Transaction - MultiSigWallet = types.MultiSigWallet + MsgCreateWallet = types.MsgCreateWallet + MsgCreateTransaction = types.MsgCreateTransaction + MsgSignTransaction = types.MsgSignTransaction + MsgCompleteTransaction = types.MsgCompleteTransaction + QueryResResolve = types.QueryResResolve + QueryResNames = types.QueryResNames + Transaction = types.Transaction + Signature = types.Signature + MultiSigWallet = types.MultiSigWallet ) diff --git a/x/multisig/handler.go b/x/multisig/handler.go index c26cac9..b074750 100755 --- a/x/multisig/handler.go +++ b/x/multisig/handler.go @@ -12,6 +12,12 @@ func NewHandler(keeper Keeper) sdk.Handler { switch msg := msg.(type) { case MsgCreateWallet: return handleMsgCreateWallet(ctx, keeper, msg) + case MsgCreateTransaction: + return handleMsgCreateTransaction(ctx, keeper, msg) + case MsgSignTransaction: + return handleMsgSignTransaction(ctx, keeper, msg) + case MsgCompleteTransaction: + return handleMsgCompleteTransaction(ctx, keeper, msg) default: errMsg := fmt.Sprintf("Unrecognized multisig Msg type: %v", msg.Type()) return sdk.ErrUnknownRequest(errMsg).Result() @@ -33,7 +39,7 @@ func handleMsgCreateWallet(ctx sdk.Context, keeper Keeper, msg MsgCreateWallet) fmt.Sprintf("Error creating new wallet: %s", err.Error()), ).Result() } - keeper.CreateWallet(ctx, wallet) + keeper.SetWallet(ctx, wallet) return sdk.Result{} } @@ -44,7 +50,55 @@ func handleMsgCreateTransaction(ctx sdk.Context, keeper Keeper, msg MsgCreateTra if !transaction.From.Empty() { return sdk.ErrUnauthorized("Transaction already exists").Result() } - transaction = NewTransaction(msg.From, msg.To, msg.Coins, ctx.BlockHeight()) - keeper.CreateTransaction(ctx, transaction) + wallet := keeper.GetWallet(ctx, msg.From.String()) + if !wallet.Address.Empty() { + return sdk.ErrUnauthorized("No registered multi-signature wallet for 'from' address").Result() + } + sigs := make([]Signature, len(wallet.PubKeys)) + for i, pubkey := range wallet.PubKeys { + var err error + sigs[i].PubKey, err = sdk.GetAccPubKeyBech32(pubkey) + if err != nil { + return sdk.ErrUnknownRequest( + fmt.Sprintf("Error creating new transaction: %s", err.Error()), + ).Result() + } + } + transaction = NewTransaction( + msg.From, + msg.To, + msg.Coins, + ctx.BlockHeight(), + sigs, + ) + keeper.SetTransaction(ctx, transaction) + return sdk.Result{} +} + +// Handle a message to sign transaction +func handleMsgSignTransaction(ctx sdk.Context, keeper Keeper, msg MsgSignTransaction) sdk.Result { + var err error + transaction := keeper.GetTransaction(ctx, msg.UUID) + if !transaction.From.Empty() { + return sdk.ErrUnauthorized("No transaction found.").Result() + } + err = transaction.AddSignature(msg.Signature) + if err != nil { + return sdk.ErrUnauthorized( + fmt.Sprintf("Failed to sign transaction: %s", err.Error()), + ).Result() + } + keeper.SetTransaction(ctx, transaction) + return sdk.Result{} +} + +// Handle a message to complete transaction +func handleMsgCompleteTransaction(ctx sdk.Context, keeper Keeper, msg MsgCompleteTransaction) sdk.Result { + transaction := keeper.GetTransaction(ctx, msg.UUID) + if !transaction.From.Empty() { + return sdk.ErrUnauthorized("No transaction found.").Result() + } + transaction.TxID = msg.TxID + keeper.SetTransaction(ctx, transaction) return sdk.Result{} } diff --git a/x/multisig/keeper.go b/x/multisig/keeper.go index 9127f9c..67f5f26 100755 --- a/x/multisig/keeper.go +++ b/x/multisig/keeper.go @@ -42,7 +42,7 @@ func (k Keeper) GetWallet(ctx sdk.Context, address string) MultiSigWallet { } // Sets the entire wallet metadata struct for a multisig wallet -func (k Keeper) CreateWallet(ctx sdk.Context, wallet MultiSigWallet) { +func (k Keeper) SetWallet(ctx sdk.Context, wallet MultiSigWallet) { address := fmt.Sprintf("wallet-%s", wallet.Address.String()) store := ctx.KVStore(k.storeKey) store.Set([]byte(address), k.cdc.MustMarshalBinaryBare(wallet)) @@ -65,7 +65,7 @@ func (k Keeper) GetTransaction(ctx sdk.Context, uid uuid.UUID) Transaction { return transaction } -func (k Keeper) CreateTransaction(ctx sdk.Context, transaction Transaction) { +func (k Keeper) SetTransaction(ctx sdk.Context, transaction Transaction) { key := fmt.Sprintf("transaction-%s", transaction.UUID) store := ctx.KVStore(k.storeKey) store.Set([]byte(key), k.cdc.MustMarshalBinaryBare(transaction)) diff --git a/x/multisig/types/msgs.go b/x/multisig/types/msgs.go index e2ffb98..ac7e406 100755 --- a/x/multisig/types/msgs.go +++ b/x/multisig/types/msgs.go @@ -108,3 +108,88 @@ func (msg MsgCreateTransaction) GetSignBytes() []byte { func (msg MsgCreateTransaction) GetSigners() []sdk.AccAddress { return []sdk.AccAddress{msg.From} } + +// MsgSignTransaction defines a SignTransaction message +type MsgSignTransaction struct { + UUID uuid.UUID `json:"uuid"` + Signature Signature `json:"signature"` +} + +// NewMsgSignTransaction is a constructor function for MsgCreateTransaction +func NewMsgSignTransaction(uid uuid.UUID, sig Signature) MsgSignTransaction { + return MsgSignTransaction{ + UUID: uid, + Signature: sig, + } +} + +// Route should return the name of the module +func (msg MsgSignTransaction) Route() string { return RouterKey } + +// Type should return the action +func (msg MsgSignTransaction) Type() string { return "sign_transaction" } + +// ValidateBasic runs stateless checks on the message +func (msg MsgSignTransaction) ValidateBasic() sdk.Error { + if len(msg.UUID) == 0 { + return sdk.ErrUnknownRequest("UUID cannot be blank") + } + if len(msg.Signature.PubKey.Bytes()) == 0 { + return sdk.ErrUnknownRequest("Pubkey cannot be blank") + } + if msg.Signature.Signature == "" { + return sdk.ErrUnknownRequest("Signature cannot be blank") + } + return nil +} + +// GetSignBytes encodes the message for signing +func (msg MsgSignTransaction) GetSignBytes() []byte { + return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(msg)) +} + +// GetSigners defines whose signature is required +func (msg MsgSignTransaction) GetSigners() []sdk.AccAddress { + return []sdk.AccAddress{} +} + +// MsgCompleteTransaction defines complete a transaction +type MsgCompleteTransaction struct { + UUID uuid.UUID `json:"uuid"` + TxID string `json:"tx_id"` +} + +// NewMsgCompleteTransaction is a constructor function for MsgCompleteTransaction +func NewMsgCompleteTransaction(uid uuid.UUID, txID string) MsgCompleteTransaction { + return MsgCompleteTransaction{ + UUID: uid, + TxID: txID, + } +} + +// Route should return the name of the module +func (msg MsgCompleteTransaction) Route() string { return RouterKey } + +// Type should return the action +func (msg MsgCompleteTransaction) Type() string { return "sign_transaction" } + +// ValidateBasic runs stateless checks on the message +func (msg MsgCompleteTransaction) ValidateBasic() sdk.Error { + if len(msg.UUID) == 0 { + return sdk.ErrUnknownRequest("UUID cannot be blank") + } + if msg.TxID == "" { + return sdk.ErrUnknownRequest("Transaction ID cannot be blank") + } + return nil +} + +// GetSignBytes encodes the message for signing +func (msg MsgCompleteTransaction) GetSignBytes() []byte { + return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(msg)) +} + +// GetSigners defines whose signature is required +func (msg MsgCompleteTransaction) GetSigners() []sdk.AccAddress { + return []sdk.AccAddress{} +} diff --git a/x/multisig/types/types.go b/x/multisig/types/types.go index 33fadb4..a2cb6f4 100755 --- a/x/multisig/types/types.go +++ b/x/multisig/types/types.go @@ -89,19 +89,32 @@ type Transaction struct { To sdk.AccAddress `json:"to_address"` Coins sdk.Coins `json:"coins"` Signatures []Signature `json:"signatures"` + TxID string `json:"tx_id"` CreatedAt int64 `json:"created_at"` // block height } -func NewTransaction(from, to sdk.AccAddress, coins sdk.Coins, height int64) Transaction { +func NewTransaction(from, to sdk.AccAddress, coins sdk.Coins, height int64, signatures []Signature) Transaction { return Transaction{ - UUID: uuid.New(), - From: from, - To: to, - Coins: coins, - CreatedAt: height, + UUID: uuid.New(), + From: from, + To: to, + Coins: coins, + CreatedAt: height, + Signatures: signatures, } } +// adds a signature to Transaction. If signature already exists, overwrite +func (t *Transaction) AddSignature(input Signature) error { + for i, sig := range t.Signatures { + if sig.PubKey == input.PubKey { + t.Signatures[i].Signature = input.Signature + return nil + } + } + return fmt.Errorf("Unable to add signature") +} + func (t Transaction) String() string { return strings.TrimSpace( fmt.Sprintf(