From 4c5c9b5add096909ec16e13357e982d5dbb35d08 Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Wed, 15 Mar 2023 19:17:34 +0100 Subject: [PATCH 01/17] rlp: support for uint256 --- rlp/decode.go | 71 ++++++++++++++++++++++++++++++++++++++++++++++ rlp/encode_test.go | 26 +++++++++++++++++ 2 files changed, 97 insertions(+) diff --git a/rlp/decode.go b/rlp/decode.go index c9b265241455..a1ef304a0a77 100644 --- a/rlp/decode.go +++ b/rlp/decode.go @@ -29,6 +29,7 @@ import ( "sync" "github.com/ethereum/go-ethereum/rlp/internal/rlpstruct" + "github.com/holiman/uint256" ) //lint:ignore ST1012 EOL is not an error. @@ -148,6 +149,7 @@ func addErrorContext(err error, ctx string) error { var ( decoderInterface = reflect.TypeOf(new(Decoder)).Elem() bigInt = reflect.TypeOf(big.Int{}) + u256Int = reflect.TypeOf(uint256.Int{}) ) func makeDecoder(typ reflect.Type, tags rlpstruct.Tags) (dec decoder, err error) { @@ -159,6 +161,10 @@ func makeDecoder(typ reflect.Type, tags rlpstruct.Tags) (dec decoder, err error) return decodeBigInt, nil case typ.AssignableTo(bigInt): return decodeBigIntNoPtr, nil + case typ.AssignableTo(reflect.PtrTo(u256Int)): + return decodeU256, nil + case typ.AssignableTo(bigInt): + return decodeU256NoPtr, nil case kind == reflect.Ptr: return makePtrDecoder(typ, tags) case reflect.PtrTo(typ).Implements(decoderInterface): @@ -235,6 +241,24 @@ func decodeBigInt(s *Stream, val reflect.Value) error { return nil } +func decodeU256NoPtr(s *Stream, val reflect.Value) error { + return decodeU256(s, val.Addr()) +} + +func decodeU256(s *Stream, val reflect.Value) error { + i := val.Interface().(*uint256.Int) + if i == nil { + i = new(uint256.Int) + val.Set(reflect.ValueOf(i)) + } + + err := s.decodeUint256(i) + if err != nil { + return wrapStreamError(err, val.Type()) + } + return nil +} + func makeListDecoder(typ reflect.Type, tag rlpstruct.Tags) (decoder, error) { etype := typ.Elem() if etype.Kind() == reflect.Uint8 && !reflect.PtrTo(etype).Implements(decoderInterface) { @@ -863,6 +887,53 @@ func (s *Stream) decodeBigInt(dst *big.Int) error { return nil } +func (s *Stream) decodeUint256(dst *uint256.Int) error { + var buffer []byte + kind, size, err := s.Kind() + switch { + case err != nil: + return err + case kind == List: + return ErrExpectedString + case kind == Byte: + buffer = s.uintbuf[:1] + buffer[0] = s.byteval + s.kind = -1 // re-arm Kind + case size == 0: + // Avoid zero-length read. + s.kind = -1 + case size <= uint64(len(s.uintbuf)): + // For integers smaller than s.uintbuf, allocating a buffer + // can be avoided. + buffer = s.uintbuf[:size] + if err := s.readFull(buffer); err != nil { + return err + } + // Reject inputs where single byte encoding should have been used. + if size == 1 && buffer[0] < 128 { + return ErrCanonSize + } + default: + // uint256 does not support >32byte integers + // Should we raise an error here or just truncate? + // TODO! + // + // For large integers, a temporary buffer is needed. + buffer = make([]byte, size) + if err := s.readFull(buffer); err != nil { + return err + } + } + + // Reject leading zero bytes. + if len(buffer) > 0 && buffer[0] == 0 { + return ErrCanonInt + } + // Set the integer bytes. + dst.SetBytes(buffer) + return nil +} + // Decode decodes a value and stores the result in the value pointed // to by val. Please see the documentation for the Decode function // to learn about the decoding rules. diff --git a/rlp/encode_test.go b/rlp/encode_test.go index 82c490a80275..4221314b4ac7 100644 --- a/rlp/encode_test.go +++ b/rlp/encode_test.go @@ -27,6 +27,7 @@ import ( "testing" "github.com/ethereum/go-ethereum/common/math" + "github.com/holiman/uint256" ) type testEncoder struct { @@ -147,6 +148,31 @@ var encTests = []encTest{ {val: big.NewInt(-1), error: "rlp: cannot encode negative big.Int"}, {val: *big.NewInt(-1), error: "rlp: cannot encode negative big.Int"}, + // uint256 + {val: uint256.NewInt(0), output: "80"}, + {val: uint256.NewInt(1), output: "01"}, + {val: uint256.NewInt(127), output: "7F"}, + {val: uint256.NewInt(128), output: "8180"}, + {val: uint256.NewInt(256), output: "820100"}, + {val: uint256.NewInt(1024), output: "820400"}, + {val: uint256.NewInt(0xFFFFFF), output: "83FFFFFF"}, + {val: uint256.NewInt(0xFFFFFFFF), output: "84FFFFFFFF"}, + {val: uint256.NewInt(0xFFFFFFFFFF), output: "85FFFFFFFFFF"}, + {val: uint256.NewInt(0xFFFFFFFFFFFF), output: "86FFFFFFFFFFFF"}, + {val: uint256.NewInt(0xFFFFFFFFFFFFFF), output: "87FFFFFFFFFFFFFF"}, + { + val: new(uint256.Int).SetBytes(unhex("102030405060708090A0B0C0D0E0F2")), + output: "8F102030405060708090A0B0C0D0E0F2", + }, + { + val: new(uint256.Int).SetBytes(unhex("0100020003000400050006000700080009000A000B000C000D000E01")), + output: "9C0100020003000400050006000700080009000A000B000C000D000E01", + }, + // non-pointer uint256.Int -- not supported + // encode_test.go:402: test 47: unexpected error: rlp: unadressable value of type uint256.Int, EncodeRLP is pointer method + //{val: *uint256.NewInt(0), output: "80"}, + //{val: *uint256.NewInt(0xFFFFFF), output: "83FFFFFF"}, + // byte arrays {val: [0]byte{}, output: "80"}, {val: [1]byte{0}, output: "00"}, From eb8c0574a0661dda1fa6c51f79e27da04f15c5f7 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Wed, 15 Mar 2023 23:55:50 +0100 Subject: [PATCH 02/17] rlp: return error for too large uint256 --- rlp/decode.go | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/rlp/decode.go b/rlp/decode.go index a1ef304a0a77..66f6e8b0c692 100644 --- a/rlp/decode.go +++ b/rlp/decode.go @@ -53,6 +53,7 @@ var ( errUintOverflow = errors.New("rlp: uint overflow") errNoPointer = errors.New("rlp: interface given to Decode must be a pointer") errDecodeIntoNil = errors.New("rlp: pointer given to Decode must not be nil") + errUint256Large = errors.New("rlp: value too large for uint256") streamPool = sync.Pool{ New: func() interface{} { return new(Stream) }, @@ -914,15 +915,7 @@ func (s *Stream) decodeUint256(dst *uint256.Int) error { return ErrCanonSize } default: - // uint256 does not support >32byte integers - // Should we raise an error here or just truncate? - // TODO! - // - // For large integers, a temporary buffer is needed. - buffer = make([]byte, size) - if err := s.readFull(buffer); err != nil { - return err - } + return errUint256Large } // Reject leading zero bytes. From 56c659e0204292a8d497994938044f83b9716d78 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Wed, 15 Mar 2023 23:56:02 +0100 Subject: [PATCH 03/17] rlp: fix decoding of non-pointer uint256 --- rlp/decode.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rlp/decode.go b/rlp/decode.go index 66f6e8b0c692..0952d217cd53 100644 --- a/rlp/decode.go +++ b/rlp/decode.go @@ -164,7 +164,7 @@ func makeDecoder(typ reflect.Type, tags rlpstruct.Tags) (dec decoder, err error) return decodeBigIntNoPtr, nil case typ.AssignableTo(reflect.PtrTo(u256Int)): return decodeU256, nil - case typ.AssignableTo(bigInt): + case typ.AssignableTo(u256Int): return decodeU256NoPtr, nil case kind == reflect.Ptr: return makePtrDecoder(typ, tags) From 0d92715dc6f91ee3cc663ad71a5485c66e518edf Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Wed, 15 Mar 2023 23:56:14 +0100 Subject: [PATCH 04/17] rlp: add uint256 tests --- rlp/decode_test.go | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/rlp/decode_test.go b/rlp/decode_test.go index dbcfcffed1a1..3928e9895dc9 100644 --- a/rlp/decode_test.go +++ b/rlp/decode_test.go @@ -28,6 +28,7 @@ import ( "testing" "github.com/ethereum/go-ethereum/common/math" + "github.com/holiman/uint256" ) func TestStreamKind(t *testing.T) { @@ -468,6 +469,10 @@ var ( veryVeryBigInt = new(big.Int).Exp(veryBigInt, big.NewInt(8), nil) ) +var ( + veryBigInt256, _ = uint256.FromBig(veryBigInt) +) + var decodeTests = []decodeTest{ // booleans {input: "01", ptr: new(bool), value: true}, @@ -541,11 +546,27 @@ var decodeTests = []decodeTest{ {input: "89FFFFFFFFFFFFFFFFFF", ptr: new(*big.Int), value: veryBigInt}, {input: "B848FFFFFFFFFFFFFFFFF800000000000000001BFFFFFFFFFFFFFFFFC8000000000000000045FFFFFFFFFFFFFFFFC800000000000000001BFFFFFFFFFFFFFFFFF8000000000000000001", ptr: new(*big.Int), value: veryVeryBigInt}, {input: "10", ptr: new(big.Int), value: *big.NewInt(16)}, // non-pointer also works + + // big int errors {input: "C0", ptr: new(*big.Int), error: "rlp: expected input string or byte for *big.Int"}, {input: "00", ptr: new(*big.Int), error: "rlp: non-canonical integer (leading zero bytes) for *big.Int"}, {input: "820001", ptr: new(*big.Int), error: "rlp: non-canonical integer (leading zero bytes) for *big.Int"}, {input: "8105", ptr: new(*big.Int), error: "rlp: non-canonical size information for *big.Int"}, + // uint256 + {input: "80", ptr: new(*uint256.Int), value: uint256.NewInt(0)}, + {input: "01", ptr: new(*uint256.Int), value: uint256.NewInt(1)}, + {input: "88FFFFFFFFFFFFFFFF", ptr: new(*uint256.Int), value: uint256.NewInt(math.MaxUint64)}, + {input: "89FFFFFFFFFFFFFFFFFF", ptr: new(*uint256.Int), value: veryBigInt256}, + {input: "10", ptr: new(uint256.Int), value: *uint256.NewInt(16)}, // non-pointer also works + + // uint256 errors + {input: "C0", ptr: new(*uint256.Int), error: "rlp: expected input string or byte for *uint256.Int"}, + {input: "00", ptr: new(*uint256.Int), error: "rlp: non-canonical integer (leading zero bytes) for *uint256.Int"}, + {input: "820001", ptr: new(*uint256.Int), error: "rlp: non-canonical integer (leading zero bytes) for *uint256.Int"}, + {input: "8105", ptr: new(*uint256.Int), error: "rlp: non-canonical size information for *uint256.Int"}, + {input: "A1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00", ptr: new(*uint256.Int), error: "rlp: value too large for uint256"}, + // structs { input: "C50583343434", From 08d450e9ffd7be9888d1117a3216bd313e7346a4 Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Thu, 16 Mar 2023 07:40:24 +0100 Subject: [PATCH 05/17] rlp: handle encoding u256 both pointer and non-pointer --- rlp/encode.go | 21 +++++++++++++++++++++ rlp/encode_test.go | 7 +++---- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/rlp/encode.go b/rlp/encode.go index a377a1ef4c99..8a6576715da9 100644 --- a/rlp/encode.go +++ b/rlp/encode.go @@ -24,6 +24,7 @@ import ( "reflect" "github.com/ethereum/go-ethereum/rlp/internal/rlpstruct" + "github.com/holiman/uint256" ) var ( @@ -144,6 +145,10 @@ func makeWriter(typ reflect.Type, ts rlpstruct.Tags) (writer, error) { return writeBigIntPtr, nil case typ.AssignableTo(bigInt): return writeBigIntNoPtr, nil + case typ.AssignableTo(reflect.PtrTo(u256Int)): + return writeU256IntPtr, nil + case typ.AssignableTo(u256Int): + return writeU256IntNoPtr, nil case kind == reflect.Ptr: return makePtrWriter(typ, ts) case reflect.PtrTo(typ).Implements(encoderInterface): @@ -206,6 +211,22 @@ func writeBigIntNoPtr(val reflect.Value, w *encBuffer) error { return nil } +func writeU256IntPtr(val reflect.Value, w *encBuffer) error { + ptr := val.Interface().(*uint256.Int) + if ptr == nil { + w.str = append(w.str, 0x80) + return nil + } + ptr.EncodeRLP(w) + return nil +} + +func writeU256IntNoPtr(val reflect.Value, w *encBuffer) error { + i := val.Interface().(uint256.Int) + i.EncodeRLP(w) + return nil +} + func writeBytes(val reflect.Value, w *encBuffer) error { w.writeBytes(val.Bytes()) return nil diff --git a/rlp/encode_test.go b/rlp/encode_test.go index 4221314b4ac7..9f9553a27363 100644 --- a/rlp/encode_test.go +++ b/rlp/encode_test.go @@ -168,10 +168,9 @@ var encTests = []encTest{ val: new(uint256.Int).SetBytes(unhex("0100020003000400050006000700080009000A000B000C000D000E01")), output: "9C0100020003000400050006000700080009000A000B000C000D000E01", }, - // non-pointer uint256.Int -- not supported - // encode_test.go:402: test 47: unexpected error: rlp: unadressable value of type uint256.Int, EncodeRLP is pointer method - //{val: *uint256.NewInt(0), output: "80"}, - //{val: *uint256.NewInt(0xFFFFFF), output: "83FFFFFF"}, + // non-pointer uint256.Int + {val: *uint256.NewInt(0), output: "80"}, + {val: *uint256.NewInt(0xFFFFFF), output: "83FFFFFF"}, // byte arrays {val: [0]byte{}, output: "80"}, From 5084e85d2080172ee8f9f2fbbfefdcc6f9ad5653 Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Thu, 16 Mar 2023 11:26:48 +0100 Subject: [PATCH 06/17] rlp: implement encoderlp for uint256.Int --- rlp/encbuffer.go | 20 ++++++++++++++++++++ rlp/encode.go | 4 ++-- rlp/encode_test.go | 1 + 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/rlp/encbuffer.go b/rlp/encbuffer.go index d2c6d93bcaea..8e7bb0e6f7db 100644 --- a/rlp/encbuffer.go +++ b/rlp/encbuffer.go @@ -17,10 +17,13 @@ package rlp import ( + "encoding/binary" "io" "math/big" "reflect" "sync" + + "github.com/holiman/uint256" ) type encBuffer struct { @@ -169,6 +172,23 @@ func (w *encBuffer) writeBigInt(i *big.Int) { } } +// writeU256Int writes i as a uint256.Int. +func (w *encBuffer) writeU256Int(z *uint256.Int) { + bitlen := z.BitLen() + if bitlen <= 64 { + w.writeUint64(z.Uint64()) + return + } + nBytes := byte((bitlen + 7) / 8) + var b [33]byte + binary.BigEndian.PutUint64(b[1:9], z[3]) + binary.BigEndian.PutUint64(b[9:17], z[2]) + binary.BigEndian.PutUint64(b[17:25], z[1]) + binary.BigEndian.PutUint64(b[25:33], z[0]) + b[32-nBytes] = 0x80 + nBytes + w.str = append(w.str, b[32-nBytes:]...) +} + // list adds a new list header to the header stack. It returns the index of the header. // Call listEnd with this index after encoding the content of the list. func (buf *encBuffer) list() int { diff --git a/rlp/encode.go b/rlp/encode.go index 8a6576715da9..0ecc14b11ce9 100644 --- a/rlp/encode.go +++ b/rlp/encode.go @@ -217,13 +217,13 @@ func writeU256IntPtr(val reflect.Value, w *encBuffer) error { w.str = append(w.str, 0x80) return nil } - ptr.EncodeRLP(w) + w.writeU256Int(ptr) return nil } func writeU256IntNoPtr(val reflect.Value, w *encBuffer) error { i := val.Interface().(uint256.Int) - i.EncodeRLP(w) + w.writeU256Int(&i) return nil } diff --git a/rlp/encode_test.go b/rlp/encode_test.go index 9f9553a27363..b753010ef7c8 100644 --- a/rlp/encode_test.go +++ b/rlp/encode_test.go @@ -326,6 +326,7 @@ var encTests = []encTest{ {val: (*[]byte)(nil), output: "80"}, {val: (*[10]byte)(nil), output: "80"}, {val: (*big.Int)(nil), output: "80"}, + {val: (*uint256.Int)(nil), output: "80"}, {val: (*[]string)(nil), output: "C0"}, {val: (*[10]string)(nil), output: "C0"}, {val: (*[]interface{})(nil), output: "C0"}, From 4712879c16d09f6ea4e5612514823df0f743b43d Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Thu, 16 Mar 2023 12:02:39 +0100 Subject: [PATCH 07/17] rlp: benchmark tests for u256 --- rlp/decode_test.go | 21 +++++++++++++++++++++ rlp/encode_test.go | 17 +++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/rlp/decode_test.go b/rlp/decode_test.go index 3928e9895dc9..07d9c579a6a4 100644 --- a/rlp/decode_test.go +++ b/rlp/decode_test.go @@ -1244,6 +1244,27 @@ func BenchmarkDecodeBigInts(b *testing.B) { } } +func BenchmarkDecodeU256Ints(b *testing.B) { + ints := make([]*uint256.Int, 200) + for i := range ints { + ints[i], _ = uint256.FromBig(math.BigPow(2, int64(i))) + } + enc, err := EncodeToBytes(ints) + if err != nil { + b.Fatal(err) + } + b.SetBytes(int64(len(enc))) + b.ReportAllocs() + b.ResetTimer() + + var out []*uint256.Int + for i := 0; i < b.N; i++ { + if err := DecodeBytes(enc, &out); err != nil { + b.Fatal(err) + } + } +} + func encodeTestSlice(n uint) []byte { s := make([]uint, n) for i := uint(0); i < n; i++ { diff --git a/rlp/encode_test.go b/rlp/encode_test.go index b753010ef7c8..f36414b9bcd0 100644 --- a/rlp/encode_test.go +++ b/rlp/encode_test.go @@ -535,6 +535,23 @@ func BenchmarkEncodeBigInts(b *testing.B) { } } +func BenchmarkEncodeU256Ints(b *testing.B) { + ints := make([]*uint256.Int, 200) + for i := range ints { + ints[i], _ = uint256.FromBig(math.BigPow(2, int64(i))) + } + out := bytes.NewBuffer(make([]byte, 0, 4096)) + b.ResetTimer() + b.ReportAllocs() + + for i := 0; i < b.N; i++ { + out.Reset() + if err := Encode(out, ints); err != nil { + b.Fatal(err) + } + } +} + func BenchmarkEncodeConcurrentInterface(b *testing.B) { type struct1 struct { A string From 6f9fcc4ca3ea4732f819404ada16a25aa5d1f308 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Thu, 16 Mar 2023 11:57:01 +0100 Subject: [PATCH 08/17] rlp: add EncoderBuffer method for Uint256 --- rlp/encbuffer.go | 9 +++++++-- rlp/encode.go | 4 ++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/rlp/encbuffer.go b/rlp/encbuffer.go index 8e7bb0e6f7db..5eb0e87f5393 100644 --- a/rlp/encbuffer.go +++ b/rlp/encbuffer.go @@ -172,8 +172,8 @@ func (w *encBuffer) writeBigInt(i *big.Int) { } } -// writeU256Int writes i as a uint256.Int. -func (w *encBuffer) writeU256Int(z *uint256.Int) { +// writeUint256 writes z as an integer. +func (w *encBuffer) writeUint256(z *uint256.Int) { bitlen := z.BitLen() if bitlen <= 64 { w.writeUint64(z.Uint64()) @@ -396,6 +396,11 @@ func (w EncoderBuffer) WriteBigInt(i *big.Int) { w.buf.writeBigInt(i) } +// WriteUint256 encodes uint256.Int as an RLP string. +func (w EncoderBuffer) WriteUint256(i *big.Int) { + w.buf.writeUint256(i) +} + // WriteBytes encodes b as an RLP string. func (w EncoderBuffer) WriteBytes(b []byte) { w.buf.writeBytes(b) diff --git a/rlp/encode.go b/rlp/encode.go index 0ecc14b11ce9..e96dd0336bb4 100644 --- a/rlp/encode.go +++ b/rlp/encode.go @@ -217,13 +217,13 @@ func writeU256IntPtr(val reflect.Value, w *encBuffer) error { w.str = append(w.str, 0x80) return nil } - w.writeU256Int(ptr) + w.writeUint256(ptr) return nil } func writeU256IntNoPtr(val reflect.Value, w *encBuffer) error { i := val.Interface().(uint256.Int) - w.writeU256Int(&i) + w.writeUint256(&i) return nil } From c5306d324d6385cbf2fdf13606de12c020b6ff45 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Thu, 16 Mar 2023 12:26:17 +0100 Subject: [PATCH 09/17] rlp: update comment --- rlp/decode.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/rlp/decode.go b/rlp/decode.go index 0952d217cd53..775c26cdef57 100644 --- a/rlp/decode.go +++ b/rlp/decode.go @@ -904,8 +904,7 @@ func (s *Stream) decodeUint256(dst *uint256.Int) error { // Avoid zero-length read. s.kind = -1 case size <= uint64(len(s.uintbuf)): - // For integers smaller than s.uintbuf, allocating a buffer - // can be avoided. + // All possible uint256 values fit into s.uintbuf. buffer = s.uintbuf[:size] if err := s.readFull(buffer); err != nil { return err From 87b7fdbb2c957360b8819f7ec07181a29145341d Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Thu, 16 Mar 2023 07:38:48 -0400 Subject: [PATCH 10/17] Update rlp/encbuffer.go --- rlp/encbuffer.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rlp/encbuffer.go b/rlp/encbuffer.go index 5eb0e87f5393..9ac4fcb2cabc 100644 --- a/rlp/encbuffer.go +++ b/rlp/encbuffer.go @@ -397,7 +397,7 @@ func (w EncoderBuffer) WriteBigInt(i *big.Int) { } // WriteUint256 encodes uint256.Int as an RLP string. -func (w EncoderBuffer) WriteUint256(i *big.Int) { +func (w EncoderBuffer) WriteUint256(i *uint256.Int) { w.buf.writeUint256(i) } From b774ecc9cab1ee821b07cc3590abb153a85317d5 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Thu, 16 Mar 2023 13:10:48 +0100 Subject: [PATCH 11/17] rlp: rename to ReadUint256 This is supposed to match ReadBytes --- rlp/decode.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/rlp/decode.go b/rlp/decode.go index 775c26cdef57..22e0d8970d18 100644 --- a/rlp/decode.go +++ b/rlp/decode.go @@ -253,7 +253,7 @@ func decodeU256(s *Stream, val reflect.Value) error { val.Set(reflect.ValueOf(i)) } - err := s.decodeUint256(i) + err := s.ReadUint256(i) if err != nil { return wrapStreamError(err, val.Type()) } @@ -888,7 +888,8 @@ func (s *Stream) decodeBigInt(dst *big.Int) error { return nil } -func (s *Stream) decodeUint256(dst *uint256.Int) error { +// ReadUint256 decodes the next value as a uint256. +func (s *Stream) ReadUint256(dst *uint256.Int) error { var buffer []byte kind, size, err := s.Kind() switch { From 6e9d483250e35aed4f338351227b3f09dbb52a43 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Thu, 16 Mar 2023 13:11:56 +0100 Subject: [PATCH 12/17] rlp/rlpgen: add support for uint256 --- rlp/rlpgen/gen.go | 51 ++++++++++++++++++++++++++++- rlp/rlpgen/gen_test.go | 2 +- rlp/rlpgen/testdata/uint256.in.txt | 10 ++++++ rlp/rlpgen/testdata/uint256.out.txt | 44 +++++++++++++++++++++++++ rlp/rlpgen/types.go | 10 ++++++ 5 files changed, 115 insertions(+), 2 deletions(-) create mode 100644 rlp/rlpgen/testdata/uint256.in.txt create mode 100644 rlp/rlpgen/testdata/uint256.out.txt diff --git a/rlp/rlpgen/gen.go b/rlp/rlpgen/gen.go index 1deb5a93c2a3..0c6586482698 100644 --- a/rlp/rlpgen/gen.go +++ b/rlp/rlpgen/gen.go @@ -283,7 +283,7 @@ func (op byteArrayOp) genDecode(ctx *genContext) (string, string) { return resultV, b.String() } -// bigIntNoPtrOp handles non-pointer big.Int. +// bigIntOp handles big.Int. // This exists because big.Int has it's own decoder operation on rlp.Stream, // but the decode method returns *big.Int, so it needs to be dereferenced. type bigIntOp struct { @@ -330,6 +330,49 @@ func (op bigIntOp) genDecode(ctx *genContext) (string, string) { return result, b.String() } +// uint256Op handles "github.com/holiman/uint256".Int +type uint256Op struct { + pointer bool +} + +func (op uint256Op) genWrite(ctx *genContext, v string) string { + var b bytes.Buffer + + dst := v + if !op.pointer { + dst = "&" + v + } + fmt.Fprintf(&b, "w.WriteUint256(%s)\n", dst) + + // Wrap with nil check. + if op.pointer { + code := b.String() + b.Reset() + fmt.Fprintf(&b, "if %s == nil {\n", v) + fmt.Fprintf(&b, " w.Write(rlp.EmptyString)") + fmt.Fprintf(&b, "} else {\n") + fmt.Fprint(&b, code) + fmt.Fprintf(&b, "}\n") + } + + return b.String() +} + +func (op uint256Op) genDecode(ctx *genContext) (string, string) { + ctx.addImport("github.com/holiman/uint256") + + var b bytes.Buffer + resultV := ctx.temp() + fmt.Fprintf(&b, "var %s uint256.Int\n", resultV) + fmt.Fprintf(&b, "if err := dec.ReadUint256(&%s); err != nil { return err }\n", resultV) + + result := resultV + if op.pointer { + result = "&" + resultV + } + return result, b.String() +} + // encoderDecoderOp handles rlp.Encoder and rlp.Decoder. // In order to be used with this, the type must implement both interfaces. // This restriction may be lifted in the future by creating separate ops for @@ -635,6 +678,9 @@ func (bctx *buildContext) makeOp(name *types.Named, typ types.Type, tags rlpstru if isBigInt(typ) { return bigIntOp{}, nil } + if isUint256(typ) { + return uint256Op{}, nil + } if typ == bctx.rawValueType { return bctx.makeRawValueOp(), nil } @@ -647,6 +693,9 @@ func (bctx *buildContext) makeOp(name *types.Named, typ types.Type, tags rlpstru if isBigInt(typ.Elem()) { return bigIntOp{pointer: true}, nil } + if isUint256(typ.Elem()) { + return uint256Op{pointer: true}, nil + } // Encoder/Decoder interfaces. if bctx.isEncoder(typ) { if bctx.isDecoder(typ) { diff --git a/rlp/rlpgen/gen_test.go b/rlp/rlpgen/gen_test.go index 241c34b6dfaa..ff3b8385ae16 100644 --- a/rlp/rlpgen/gen_test.go +++ b/rlp/rlpgen/gen_test.go @@ -47,7 +47,7 @@ func init() { } } -var tests = []string{"uints", "nil", "rawvalue", "optional", "bigint"} +var tests = []string{"uints", "nil", "rawvalue", "optional", "bigint", "uint256"} func TestOutput(t *testing.T) { for _, test := range tests { diff --git a/rlp/rlpgen/testdata/uint256.in.txt b/rlp/rlpgen/testdata/uint256.in.txt new file mode 100644 index 000000000000..ed16e0a7882f --- /dev/null +++ b/rlp/rlpgen/testdata/uint256.in.txt @@ -0,0 +1,10 @@ +// -*- mode: go -*- + +package test + +import "github.com/holiman/uint256" + +type Test struct { + Int *uint256.Int + IntNoPtr uint256.Int +} diff --git a/rlp/rlpgen/testdata/uint256.out.txt b/rlp/rlpgen/testdata/uint256.out.txt new file mode 100644 index 000000000000..0ca37f2d1903 --- /dev/null +++ b/rlp/rlpgen/testdata/uint256.out.txt @@ -0,0 +1,44 @@ +package test + +import "github.com/ethereum/go-ethereum/rlp" +import "github.com/holiman/uint256" +import "io" + +func (obj *Test) EncodeRLP(_w io.Writer) error { + w := rlp.NewEncoderBuffer(_w) + _tmp0 := w.List() + if obj.Int == nil { + w.Write(rlp.EmptyString) + } else { + w.WriteUint256(obj.Int) + } + w.WriteUint256(&obj.IntNoPtr) + w.ListEnd(_tmp0) + return w.Flush() +} + +func (obj *Test) DecodeRLP(dec *rlp.Stream) error { + var _tmp0 Test + { + if _, err := dec.List(); err != nil { + return err + } + // Int: + var _tmp1 uint256.Int + if err := dec.DecodeUint256(&_tmp1); err != nil { + return err + } + _tmp0.Int = &_tmp1 + // IntNoPtr: + var _tmp2 uint256.Int + if err := dec.DecodeUint256(&_tmp2); err != nil { + return err + } + _tmp0.IntNoPtr = _tmp2 + if err := dec.ListEnd(); err != nil { + return err + } + } + *obj = _tmp0 + return nil +} diff --git a/rlp/rlpgen/types.go b/rlp/rlpgen/types.go index 19694262e54e..ea7dc96d8813 100644 --- a/rlp/rlpgen/types.go +++ b/rlp/rlpgen/types.go @@ -97,6 +97,16 @@ func isBigInt(typ types.Type) bool { return name.Pkg().Path() == "math/big" && name.Name() == "Int" } +// isUint256 checks whether 'typ' is "github.com/holiman/uint256".Int. +func isUint256(typ types.Type) bool { + named, ok := typ.(*types.Named) + if !ok { + return false + } + name := named.Obj() + return name.Pkg().Path() == "github.com/holiman/uint256" && name.Name() == "Int" +} + // isByte checks whether the underlying type of 'typ' is uint8. func isByte(typ types.Type) bool { basic, ok := resolveUnderlying(typ).(*types.Basic) From 0a2fdfbc17efab76cfaa2e5128467784f4f14bc4 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Thu, 16 Mar 2023 13:50:32 +0100 Subject: [PATCH 13/17] rlp: add failing test for array encoding --- rlp/encode_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/rlp/encode_test.go b/rlp/encode_test.go index f36414b9bcd0..d4f5e88e1fbd 100644 --- a/rlp/encode_test.go +++ b/rlp/encode_test.go @@ -281,6 +281,10 @@ var encTests = []encTest{ output: "F90200CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376", }, + // arrays + {val: [4]uint32{1, 2, 3, 4}, output: "8401020304"}, + {val: [4]uint64{1, 2, 3, 4}, output: "8401020304"}, + // RawValue {val: RawValue(unhex("01")), output: "01"}, {val: RawValue(unhex("82FFFF")), output: "82FFFF"}, From 89298f70139eb83ecd2a729b32a55c42e9d1ac16 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Thu, 16 Mar 2023 13:57:29 +0100 Subject: [PATCH 14/17] rlp: fix array test --- rlp/encode_test.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/rlp/encode_test.go b/rlp/encode_test.go index d4f5e88e1fbd..02be47d0ef1b 100644 --- a/rlp/encode_test.go +++ b/rlp/encode_test.go @@ -281,9 +281,11 @@ var encTests = []encTest{ output: "F90200CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376", }, - // arrays - {val: [4]uint32{1, 2, 3, 4}, output: "8401020304"}, - {val: [4]uint64{1, 2, 3, 4}, output: "8401020304"}, + // Non-byte arrays are encoded as lists. + // Note that it is important to test [4]uint64 specifically, + // because that's the underlying type of uint256.Int. + {val: [4]uint32{1, 2, 3, 4}, output: "C401020304"}, + {val: [4]uint64{1, 2, 3, 4}, output: "C401020304"}, // RawValue {val: RawValue(unhex("01")), output: "01"}, From 8a6cbf8912c9b67cfb18ae07729ef03c4de05512 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Thu, 16 Mar 2023 13:57:38 +0100 Subject: [PATCH 15/17] rlp: check for uint256 more strictly --- rlp/encode.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rlp/encode.go b/rlp/encode.go index e96dd0336bb4..3fac0bd2d3c2 100644 --- a/rlp/encode.go +++ b/rlp/encode.go @@ -145,9 +145,9 @@ func makeWriter(typ reflect.Type, ts rlpstruct.Tags) (writer, error) { return writeBigIntPtr, nil case typ.AssignableTo(bigInt): return writeBigIntNoPtr, nil - case typ.AssignableTo(reflect.PtrTo(u256Int)): + case typ == reflect.PtrTo(u256Int): return writeU256IntPtr, nil - case typ.AssignableTo(u256Int): + case typ == u256Int: return writeU256IntNoPtr, nil case kind == reflect.Ptr: return makePtrWriter(typ, ts) From aa376f8df8cc254a51f776fa331ee9234f93917f Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Thu, 16 Mar 2023 13:58:15 +0100 Subject: [PATCH 16/17] rlp: check for uint256 more strictly in decoder --- rlp/decode.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rlp/decode.go b/rlp/decode.go index 22e0d8970d18..c9b50e8c1879 100644 --- a/rlp/decode.go +++ b/rlp/decode.go @@ -162,9 +162,9 @@ func makeDecoder(typ reflect.Type, tags rlpstruct.Tags) (dec decoder, err error) return decodeBigInt, nil case typ.AssignableTo(bigInt): return decodeBigIntNoPtr, nil - case typ.AssignableTo(reflect.PtrTo(u256Int)): + case typ == reflect.PtrTo(u256Int): return decodeU256, nil - case typ.AssignableTo(u256Int): + case typ == u256Int: return decodeU256NoPtr, nil case kind == reflect.Ptr: return makePtrDecoder(typ, tags) From 4ad1faabeef9fbe4c3c6b6e6a12b91b4a7c4ea10 Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Thu, 16 Mar 2023 20:20:54 +0100 Subject: [PATCH 17/17] rlp/rlpgen: update uint256 output after renaming of method --- rlp/rlpgen/testdata/uint256.out.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rlp/rlpgen/testdata/uint256.out.txt b/rlp/rlpgen/testdata/uint256.out.txt index 0ca37f2d1903..5e6d3ed992cd 100644 --- a/rlp/rlpgen/testdata/uint256.out.txt +++ b/rlp/rlpgen/testdata/uint256.out.txt @@ -25,13 +25,13 @@ func (obj *Test) DecodeRLP(dec *rlp.Stream) error { } // Int: var _tmp1 uint256.Int - if err := dec.DecodeUint256(&_tmp1); err != nil { + if err := dec.ReadUint256(&_tmp1); err != nil { return err } _tmp0.Int = &_tmp1 // IntNoPtr: var _tmp2 uint256.Int - if err := dec.DecodeUint256(&_tmp2); err != nil { + if err := dec.ReadUint256(&_tmp2); err != nil { return err } _tmp0.IntNoPtr = _tmp2