-
Notifications
You must be signed in to change notification settings - Fork 35
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
[WIP] Add broadcast confirm sig logic #188
base: develop
Are you sure you want to change the base?
Changes from all commits
b470d79
a3c1744
584152c
00a090c
c828a0d
19ce7cd
f648761
84e5867
620d212
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
package tx | ||
|
||
import ( | ||
"fmt" | ||
"github.com/FourthState/plasma-mvp-sidechain/msgs" | ||
"github.com/FourthState/plasma-mvp-sidechain/plasma" | ||
"github.com/cosmos/cosmos-sdk/client/context" | ||
"github.com/ethereum/go-ethereum/rlp" | ||
"github.com/spf13/cobra" | ||
"github.com/spf13/viper" | ||
"strings" | ||
) | ||
|
||
func BroadcastSigsCmd() *cobra.Command { | ||
includeCmd.Flags().Bool(asyncF, false, "wait for transaction commitment synchronously") | ||
return includeCmd | ||
} | ||
|
||
var broadcastSigsCmd = &cobra.Command{ | ||
Use: "broadcast-sigs <input1, input2>", | ||
Short: "Broadcast confirm signatures tied to input", | ||
Long: `Broadcasts confirm signatures tied to an input without spending funds. | ||
Inputs take the format: | ||
(blknum0.txindex0.oindex0.depositnonce0)::(blknum1.txindex1.oindex1.depositnonce1) | ||
|
||
Example usage: | ||
plasmacli broadcast-sigs <input1> | ||
plasmacli broadcast-sigs <input1, input2> | ||
`, | ||
Args: cobra.MaximumNArgs(1), | ||
RunE: func(cmd *cobra.Command, args []string) error { | ||
viper.BindPFlags(cmd.Flags()) | ||
ctx := context.NewCLIContext() | ||
|
||
// parse inputs | ||
var inputStrs []string | ||
values := args[0] | ||
|
||
inputTokens := strings.Split(strings.TrimSpace(values), "::") | ||
if len(inputTokens) > 2 { | ||
return fmt.Errorf("2 or fewer inputs must be specified") | ||
} | ||
for _, token := range inputTokens { | ||
inputStrs = append(inputStrs, strings.TrimSpace(token)) | ||
} | ||
|
||
// retrieve positions | ||
var positions []plasma.Position | ||
for _, token := range inputStrs { | ||
position, err := plasma.FromPositionString(token) | ||
if err != nil { | ||
return fmt.Errorf("error parsing position from string: %s", err) | ||
} | ||
positions = append(positions, position) | ||
} | ||
|
||
// get confirmation signatures from local storage | ||
confirmSignatures := getConfirmSignatures(positions) | ||
|
||
// build transaction | ||
// create non-nil inputs with null signatures | ||
input1 := plasma.Input{} | ||
if len(positions) >= 1 { | ||
input1.Position = positions[0] | ||
input1.Signature = [65]byte{1} | ||
input1.ConfirmSignatures = confirmSignatures[0] | ||
} | ||
|
||
input2 := plasma.Input{} | ||
if len(positions) >= 2 { | ||
input2.Position = positions[1] | ||
input2.Signature = [65]byte{1} | ||
input2.ConfirmSignatures = confirmSignatures[1] | ||
} | ||
|
||
cmd.SilenceUsage = true | ||
|
||
msg := msgs.ConfirmSigMsg{ | ||
Input1: input1, | ||
Input2: input2, | ||
} | ||
if err := msg.ValidateBasic(); err != nil { | ||
return fmt.Errorf("failed on validating inputs. If you didn't provide the inputs please open an issue on github. error: %s", err) | ||
} | ||
|
||
txBytes, err := rlp.EncodeToBytes(&msg) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
// broadcast to the node | ||
if viper.GetBool(asyncF) { | ||
if _, err := ctx.BroadcastTxAsync(txBytes); err != nil { | ||
return err | ||
} | ||
} else { | ||
res, err := ctx.BroadcastTxAndAwaitCommit(txBytes) | ||
if err != nil { | ||
return err | ||
} | ||
fmt.Printf("Committed at block %d. Hash %s\n", res.Height, res.TxHash) | ||
} | ||
|
||
return nil | ||
}, | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -34,6 +34,9 @@ func NewAnteHandler(ds store.DataStore, client plasmaConn) sdk.AnteHandler { | |
case "spend_utxo": | ||
spendMsg := msg.(msgs.SpendMsg) | ||
return spendMsgAnteHandler(ctx, ds, spendMsg, client) | ||
case "confirm_sig": | ||
confirmSigMsg := msg.(msgs.ConfirmSigMsg) | ||
return confirmSigAnteHandler(ctx, ds, confirmSigMsg, client) | ||
default: | ||
return ctx, ErrInvalidTransaction("msg is not of type SpendMsg or IncludeDepositMsg").Result(), true | ||
} | ||
|
@@ -58,7 +61,7 @@ func spendMsgAnteHandler(ctx sdk.Context, ds store.DataStore, spendMsg msgs.Spen | |
|
||
/* validate inputs */ | ||
for i, signer := range signers { | ||
amt, res := validateInput(ctx, ds, spendMsg.Inputs[i], common.BytesToAddress(signer), client) | ||
amt, res := validateSpendInput(ctx, ds, spendMsg.Inputs[i], common.BytesToAddress(signer), client) | ||
if !res.IsOK() { | ||
return ctx, res, true | ||
} | ||
|
@@ -85,7 +88,7 @@ func spendMsgAnteHandler(ctx sdk.Context, ds store.DataStore, spendMsg msgs.Spen | |
} | ||
|
||
// validates the inputs against the output store and returns the amount of the respective input | ||
func validateInput(ctx sdk.Context, ds store.DataStore, input plasma.Input, signer common.Address, client plasmaConn) (*big.Int, sdk.Result) { | ||
func validateSpendInput(ctx sdk.Context, ds store.DataStore, input plasma.Input, signer common.Address, client plasmaConn) (*big.Int, sdk.Result) { | ||
var amt *big.Int | ||
|
||
// inputUTXO must be owned by the signer due to the prefix so we do not need to | ||
|
@@ -185,3 +188,60 @@ func includeDepositAnteHandler(ctx sdk.Context, ds store.DataStore, msg msgs.Inc | |
} | ||
return ctx, sdk.Result{}, false | ||
} | ||
|
||
// validates that the confirmSig msg is valid given the current plasma sidechain state | ||
func confirmSigAnteHandler(ctx sdk.Context, ds store.DataStore, confirmSigMsg msgs.ConfirmSigMsg, client plasmaConn) (newCtx sdk.Context, res sdk.Result, abort bool) { | ||
|
||
res = validateConfirmSigMsgInput(ctx, ds, confirmSigMsg.Input1, client) | ||
if !res.IsOK() { | ||
return ctx, res, true | ||
} | ||
|
||
res = validateConfirmSigMsgInput(ctx, ds, confirmSigMsg.Input2, client) | ||
if !res.IsOK() { | ||
return ctx, res, true | ||
} | ||
|
||
return ctx, sdk.Result{}, false | ||
} | ||
|
||
// validates the inputs against the output store | ||
func validateConfirmSigMsgInput(ctx sdk.Context, ds store.DataStore, input plasma.Input, client plasmaConn) sdk.Result { | ||
|
||
if input.Signature == [65]byte{} { | ||
return sdk.Result{} | ||
} | ||
if input.Signature != [65]byte{1} { | ||
return ErrInvalidSignature("invalid signature provided").Result() | ||
} | ||
|
||
_, ok := ds.GetOutput(ctx, input.Position) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. do we need this if we're already doing |
||
if !ok { | ||
return ErrInvalidInput("input, %v, does not exist", input.Position).Result() | ||
} | ||
|
||
// validate inputs/confirmation signatures if not a fee utxo or deposit utxo | ||
if !input.IsDeposit() && !input.IsFee() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If the input is a deposit or fee, we should not return |
||
tx, ok := ds.GetTxWithPosition(ctx, input.Position) | ||
if !ok { | ||
return sdk.ErrInternal(fmt.Sprintf("failed to retrieve the transaction that input with position %s belongs to", input.Position)).Result() | ||
} | ||
|
||
res := validateConfirmSignatures(ctx, ds, input, tx) | ||
if !res.IsOK() { | ||
return res | ||
} | ||
|
||
// check if the parent utxo has exited | ||
for _, in := range tx.Transaction.Inputs { | ||
exited, err := client.HasTxExited(ds.PlasmaBlockHeight(ctx), in.Position) | ||
if err != nil { | ||
return ErrInvalidInput(fmt.Sprintf("failed to retrieve exit information on input, %v", in.Position)).Result() | ||
} else if exited { | ||
return ErrExitedInput(fmt.Sprintf("a parent of the input has exited. Position: %v", in.Position)).Result() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can we make this message better w.r.t confirm signatures? like 'cannot include confirm signatures of a input transaction that has exited..." |
||
} | ||
} | ||
} | ||
|
||
return sdk.Result{} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Although the anteHandler for this msg will fail if the sig is wrong, we should do client side validation here before sending off the message.