From 409ebf035b3b21cadf79aebb97cc61d6bb679268 Mon Sep 17 00:00:00 2001 From: Snezhkko Date: Sun, 2 Nov 2025 16:04:39 +0200 Subject: [PATCH] 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) + } + }) + } +}