From e7f46d0ec6d3067c7aeaca84fd7494a0a680d578 Mon Sep 17 00:00:00 2001 From: Snezhkko Date: Sun, 2 Nov 2025 16:04:02 +0200 Subject: [PATCH 1/3] Update cliui.go --- signer/core/cliui.go | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/signer/core/cliui.go b/signer/core/cliui.go index e04077865d..23f4eae319 100644 --- a/signer/core/cliui.go +++ b/signer/core/cliui.go @@ -18,6 +18,7 @@ package core import ( "bufio" + "bytes" "context" "encoding/json" "fmt" @@ -142,10 +143,32 @@ func (ui *CommandlineUI) ApproveTx(request *SignTxRequest) (SignTxResponse, erro fmt.Printf(" %v\n", bh) } } - if request.Transaction.Data != nil { - d := *request.Transaction.Data - if len(d) > 0 { - fmt.Printf("data: %v\n", hexutil.Encode(d)) + { + var ( + data []byte + inputSet bool + dataSet bool + ) + if request.Transaction.Input != nil { + data = *request.Transaction.Input + inputSet = true + } + if request.Transaction.Data != nil { + dataSet = true + if !inputSet { + data = *request.Transaction.Data + } + } + if inputSet && dataSet && !bytes.Equal(*request.Transaction.Input, *request.Transaction.Data) { + fmt.Printf("input: %v\n", hexutil.Encode(*request.Transaction.Input)) + fmt.Printf("data: %v\n", hexutil.Encode(*request.Transaction.Data)) + fmt.Printf("WARNING: both input and data provided and differ; input will be used\n") + } else if len(data) > 0 { + label := "data" + if inputSet { + label = "input" + } + fmt.Printf("%s: %v\n", label, hexutil.Encode(data)) } } if request.Callinfo != nil { From c0b4b2bf79e01728c0bebc95fd5aa33371670a00 Mon Sep 17 00:00:00 2001 From: Snezhkko Date: Sun, 2 Nov 2025 16:04:13 +0200 Subject: [PATCH 2/3] Update api.go --- signer/core/api.go | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/signer/core/api.go b/signer/core/api.go index 12acf925f0..d4d3857794 100644 --- a/signer/core/api.go +++ b/signer/core/api.go @@ -497,19 +497,21 @@ func logDiff(original *SignTxRequest, new *SignTxResponse) bool { modified = true log.Info("Value changed by UI", "was", v0, "is", v1) } - if d0, d1 := original.Transaction.Data, new.Transaction.Data; d0 != d1 { - d0s := "" - d1s := "" - if d0 != nil { - d0s = hexutil.Encode(*d0) - } - if d1 != nil { - d1s = hexutil.Encode(*d1) - } - if d1s != d0s { - modified = true - log.Info("Data changed by UI", "was", d0s, "is", d1s) - } + // Compare effective calldata (prefer Input over Data) + var oldData, newData []byte + if original.Transaction.Input != nil { + oldData = *original.Transaction.Input + } else if original.Transaction.Data != nil { + oldData = *original.Transaction.Data + } + if new.Transaction.Input != nil { + newData = *new.Transaction.Input + } else if new.Transaction.Data != nil { + newData = *new.Transaction.Data + } + if !bytes.Equal(oldData, newData) { + modified = true + log.Info("Data changed by UI", "was", hexutil.Encode(oldData), "is", hexutil.Encode(newData)) } if n0, n1 := original.Transaction.Nonce, new.Transaction.Nonce; n0 != n1 { modified = true From 409ebf035b3b21cadf79aebb97cc61d6bb679268 Mon Sep 17 00:00:00 2001 From: Snezhkko Date: Sun, 2 Nov 2025 16:04:39 +0200 Subject: [PATCH 3/3] Create cliui_calldata_test.go --- signer/core/cliui_calldata_test.go | 180 +++++++++++++++++++++++++++++ 1 file changed, 180 insertions(+) create mode 100644 signer/core/cliui_calldata_test.go diff --git a/signer/core/cliui_calldata_test.go b/signer/core/cliui_calldata_test.go new file mode 100644 index 0000000000..6fb62eb7d3 --- /dev/null +++ b/signer/core/cliui_calldata_test.go @@ -0,0 +1,180 @@ +package core + +import ( + "bufio" + "bytes" + "os" + "strings" + "testing" + + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/signer/core/apitypes" +) + +func captureOutput(t *testing.T, fn func()) string { + t.Helper() + old := os.Stdout + r, w, err := os.Pipe() + if err != nil { + t.Fatalf("pipe error: %v", err) + } + os.Stdout = w + var buf bytes.Buffer + done := make(chan struct{}) + go func() { + _, _ = buf.ReadFrom(r) + close(done) + }() + fn() + _ = w.Close() + <-done + os.Stdout = old + return buf.String() +} + +func TestApproveTx_DisplaysEffectiveCalldata(t *testing.T) { + mkUI := func(input string) *CommandlineUI { + return &CommandlineUI{in: bufio.NewReader(strings.NewReader(input))} + } + hex := func(b []byte) string { return hexutil.Encode(b) } + // helper to create *hexutil.Bytes + hx := func(b []byte) *hexutil.Bytes { v := hexutil.Bytes(b); return &v } + // zero 1559 fee fields to avoid GasPrice nil deref in CLI + zeroFee := func(a *apitypes.SendTxArgs) { + z := new(hexutil.Big) + a.MaxFeePerGas = z + a.MaxPriorityFeePerGas = z + } + + cases := []struct { + name string + args apitypes.SendTxArgs + wantContains []string + wantNotExists []string + }{ + { + name: "only input", + args: func() apitypes.SendTxArgs { + a := apitypes.SendTxArgs{Input: hx([]byte{0x01, 0x02})} + zeroFee(&a) + return a + }(), + wantContains: []string{ + "input:", + hex([]byte{0x01, 0x02}), + }, + wantNotExists: []string{"WARNING: both input and data provided and differ"}, + }, + { + name: "only data", + args: func() apitypes.SendTxArgs { a := apitypes.SendTxArgs{Data: hx([]byte{0x0a})}; zeroFee(&a); return a }(), + wantContains: []string{ + "data:", + hex([]byte{0x0a}), + }, + wantNotExists: []string{"WARNING: both input and data provided and differ"}, + }, + { + name: "both equal", + args: func() apitypes.SendTxArgs { + b := hexutil.Bytes([]byte{0xaa, 0xbb}) + a := apitypes.SendTxArgs{Input: &b, Data: &b} + zeroFee(&a) + return a + }(), + wantContains: []string{ + "input:", + hex([]byte{0xaa, 0xbb}), + }, + wantNotExists: []string{"WARNING: both input and data provided and differ", "data:"}, + }, + { + name: "both different", + args: func() apitypes.SendTxArgs { + a := apitypes.SendTxArgs{Input: hx([]byte{0x01}), Data: hx([]byte{0x02})} + zeroFee(&a) + return a + }(), + wantContains: []string{ + "input:", + "data:", + "WARNING: both input and data provided and differ", + hex([]byte{0x01}), + hex([]byte{0x02}), + }, + }, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + ui := mkUI("y\n") + req := &SignTxRequest{Transaction: tc.args} + out := captureOutput(t, func() { + _, err := ui.ApproveTx(req) + if err != nil { + t.Fatalf("ApproveTx error: %v", err) + } + }) + for _, s := range tc.wantContains { + if !strings.Contains(out, s) { + t.Fatalf("output does not contain %q. Output:\n%s", s, out) + } + } + for _, s := range tc.wantNotExists { + if strings.Contains(out, s) { + t.Fatalf("output should not contain %q. Output:\n%s", s, out) + } + } + }) + } +} + +func TestLogDiff_EffectiveCalldata(t *testing.T) { + mk := func(inOld, dataOld, inNew, dataNew []byte) (SignTxRequest, SignTxResponse) { + var ( + inO, dO, inN, dN *hexutil.Bytes + ) + if inOld != nil { + v := hexutil.Bytes(inOld) + inO = &v + } + if dataOld != nil { + v := hexutil.Bytes(dataOld) + dO = &v + } + if inNew != nil { + v := hexutil.Bytes(inNew) + inN = &v + } + if dataNew != nil { + v := hexutil.Bytes(dataNew) + dN = &v + } + return SignTxRequest{Transaction: apitypes.SendTxArgs{Input: inO, Data: dO}}, SignTxResponse{Transaction: apitypes.SendTxArgs{Input: inN, Data: dN}} + } + cases := []struct { + name string + inOld []byte + dataOld []byte + inNew []byte + dataNew []byte + modified bool + }{ + {"only input unchanged", []byte{0x01}, nil, []byte{0x01}, nil, false}, + {"only data unchanged", nil, []byte{0x02}, nil, []byte{0x02}, false}, + {"both equal unchanged", []byte{0x0a}, []byte{0x0a}, []byte{0x0a}, []byte{0x0a}, false}, + {"effective changed (input differs)", []byte{0x01}, nil, []byte{0x02}, nil, true}, + {"effective changed (data differs, no input)", nil, []byte{0x01}, nil, []byte{0x02}, true}, + {"effective equal though underlying fields differ", []byte{0xaa}, []byte{0xbb}, []byte{0xaa}, []byte{0xbb}, false}, + {"both set but only new input changes", []byte{0x01}, []byte{0x01}, []byte{0x02}, []byte{0x01}, true}, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + orig, nw := mk(tc.inOld, tc.dataOld, tc.inNew, tc.dataNew) + m := logDiff(&orig, &nw) + if m != tc.modified { + t.Fatalf("modified mismatch: have %v want %v", m, tc.modified) + } + }) + } +}