Skip to content

Commit 9ed36b6

Browse files
authored
feat: Add support for named types with underlying basic types (#236)
## Why this should be merged The current rlpgen partially supports named types with basic underlying, and it generates the rlp without correct conversion i.e: `w.WriteUint64(obj.Uint64NewT)` This PR adds the full support it ## How this works Adds check for `isNamedWithBasicUnderlying` and then returns a `op` with `makeNamedBasicOp` which is basically a `basicOp` but with the `typ` refers to the named type so that it can correctly encode decode with the named type. ## How this was tested Added UT tests --------- Signed-off-by: Ceyhun Onur <ceyhunonur54@gmail.com>
1 parent 910e897 commit 9ed36b6

File tree

5 files changed

+126
-1
lines changed

5 files changed

+126
-1
lines changed

rlp/rlpgen/gen.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -687,6 +687,12 @@ func (bctx *buildContext) makeOp(name *types.Named, typ types.Type, tags rlpstru
687687
if bctx.isDecoder(typ) {
688688
return nil, fmt.Errorf("type %v implements rlp.Decoder with non-pointer receiver", typ)
689689
}
690+
if hasBasicUnderlying(typ) {
691+
// libevm: named types are reduced to their underlying basic type in this loop.
692+
// We're handling named types here by passing the named type as the main type.
693+
// See [named.libevm.go] for more details.
694+
return bctx.makeNamedBasicOp(typ)
695+
}
690696
// TODO: same check for encoder?
691697
return bctx.makeOp(typ, typ.Underlying(), tags)
692698
case *types.Pointer:

rlp/rlpgen/gen_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ func init() {
4747
}
4848
}
4949

50-
var tests = []string{"uints", "nil", "rawvalue", "optional", "bigint", "uint256", "alias"}
50+
var tests = []string{"uints", "nil", "rawvalue", "optional", "bigint", "uint256", "alias", "named.libevm"}
5151

5252
func TestOutput(t *testing.T) {
5353
for _, test := range tests {

rlp/rlpgen/named.libevm.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// Copyright 2025 the libevm authors.
2+
//
3+
// The libevm additions to go-ethereum are free software: you can redistribute
4+
// them and/or modify them under the terms of the GNU Lesser General Public License
5+
// as published by the Free Software Foundation, either version 3 of the License,
6+
// or (at your option) any later version.
7+
//
8+
// The libevm additions are distributed in the hope that they will be useful,
9+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
10+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
11+
// General Public License for more details.
12+
//
13+
// You should have received a copy of the GNU Lesser General Public License
14+
// along with the go-ethereum library. If not, see
15+
// <http://www.gnu.org/licenses/>.
16+
17+
package main
18+
19+
import (
20+
"fmt"
21+
"go/types"
22+
)
23+
24+
// makeNamedBasicOp is a convenience wrapper for basicOp.
25+
// It returns a basicOp with the named type as the main type instead of the underlying basic type.
26+
func (bctx *buildContext) makeNamedBasicOp(named *types.Named) (op, error) {
27+
underlying := named.Underlying()
28+
basic, ok := underlying.(*types.Basic)
29+
if !ok {
30+
return nil, fmt.Errorf("expected basic type, got %T", underlying)
31+
}
32+
33+
// We use basic op because it actually supports necessary conversions (through writeNeedsConversion and decodeNeedsConversion)
34+
// for named types.
35+
// The only problem with that is it does not support the named type as the main type.
36+
// So we use the named type as the main type instead of the underlying basic type.
37+
baseOp, err := bctx.makeBasicOp(basic)
38+
if err != nil {
39+
return nil, err
40+
}
41+
42+
op, ok := baseOp.(basicOp)
43+
if !ok {
44+
return nil, fmt.Errorf("expected basicOp, got %T", baseOp)
45+
}
46+
op.typ = named
47+
48+
return op, nil
49+
}
50+
51+
// hasBasicUnderlying checks whether `named` has an underlying basic type.
52+
func hasBasicUnderlying(named *types.Named) bool {
53+
_, ok := named.Underlying().(*types.Basic)
54+
return ok
55+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// -*- mode: go -*-
2+
3+
package test
4+
5+
type (
6+
BoolT bool
7+
Uint64T uint64
8+
StringT string
9+
)
10+
11+
type Test struct {
12+
BoolNewT BoolT
13+
Uint64NewT Uint64T
14+
StringNewT StringT
15+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package test
2+
3+
import "github.com/ava-labs/libevm/rlp"
4+
import "io"
5+
6+
func (obj *Test) EncodeRLP(_w io.Writer) error {
7+
w := rlp.NewEncoderBuffer(_w)
8+
_tmp0 := w.List()
9+
w.WriteBool(bool(obj.BoolNewT))
10+
w.WriteUint64(uint64(obj.Uint64NewT))
11+
w.WriteString(string(obj.StringNewT))
12+
w.ListEnd(_tmp0)
13+
return w.Flush()
14+
}
15+
16+
func (obj *Test) DecodeRLP(dec *rlp.Stream) error {
17+
var _tmp0 Test
18+
{
19+
if _, err := dec.List(); err != nil {
20+
return err
21+
}
22+
// BoolNewT:
23+
_tmp1, err := dec.Bool()
24+
if err != nil {
25+
return err
26+
}
27+
_tmp2 := BoolT(_tmp1)
28+
_tmp0.BoolNewT = _tmp2
29+
// Uint64NewT:
30+
_tmp3, err := dec.Uint64()
31+
if err != nil {
32+
return err
33+
}
34+
_tmp4 := Uint64T(_tmp3)
35+
_tmp0.Uint64NewT = _tmp4
36+
// StringNewT:
37+
_tmp5, err := dec.String()
38+
if err != nil {
39+
return err
40+
}
41+
_tmp6 := StringT(_tmp5)
42+
_tmp0.StringNewT = _tmp6
43+
if err := dec.ListEnd(); err != nil {
44+
return err
45+
}
46+
}
47+
*obj = _tmp0
48+
return nil
49+
}

0 commit comments

Comments
 (0)