Skip to content

Commit 19e61a2

Browse files
atharrva01adecaro
authored andcommitted
feat(evm): network methods, submission and finality (#2094)
Signed-off-by: atharrva01 <atharvaborade568@gmail.com>
1 parent 8ab1d45 commit 19e61a2

15 files changed

Lines changed: 2479 additions & 53 deletions

File tree

x/token/services/network/evm/abi/abi.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,3 +119,51 @@ func readWordAsLength(word []byte, what string) (int, error) {
119119

120120
// maxInt is the largest value of the platform int, used to bound-check ABI lengths before conversion.
121121
const maxInt = int(^uint(0) >> 1)
122+
123+
// DecodeBytes32 decodes a single bytes32 return value.
124+
func DecodeBytes32(ret []byte) ([32]byte, error) {
125+
var out [32]byte
126+
if len(ret) < wordLength {
127+
return out, errors.Errorf("abi: bytes32 return too short: %d bytes", len(ret))
128+
}
129+
copy(out[:], ret[:wordLength])
130+
131+
return out, nil
132+
}
133+
134+
// DecodeBoolArray decodes a bool[] return value: a head word holding the offset to the tail, then at
135+
// the tail a length word followed by one word per element (zero is false, anything else true).
136+
func DecodeBoolArray(ret []byte) ([]bool, error) {
137+
if len(ret) < wordLength {
138+
return nil, errors.Errorf("abi: bool array return too short for offset word: %d bytes", len(ret))
139+
}
140+
offset, err := readWordAsLength(ret[:wordLength], "offset")
141+
if err != nil {
142+
return nil, err
143+
}
144+
if offset > len(ret)-wordLength {
145+
return nil, errors.Errorf("abi: bool array offset %d out of bounds (len %d)", offset, len(ret))
146+
}
147+
length, err := readWordAsLength(ret[offset:offset+wordLength], "length")
148+
if err != nil {
149+
return nil, err
150+
}
151+
body := ret[offset+wordLength:]
152+
if length > len(body)/wordLength {
153+
return nil, errors.Errorf("abi: bool array length %d exceeds the %d available words", length, len(body)/wordLength)
154+
}
155+
156+
out := make([]bool, length)
157+
for i := range length {
158+
word := body[i*wordLength : (i+1)*wordLength]
159+
for _, b := range word {
160+
if b != 0 {
161+
out[i] = true
162+
163+
break
164+
}
165+
}
166+
}
167+
168+
return out, nil
169+
}

x/token/services/network/evm/driver.go

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"github.com/LFDT-Panurus/panurus/token/services/config"
1313
"github.com/LFDT-Panurus/panurus/token/services/logging"
1414
"github.com/LFDT-Panurus/panurus/token/services/network/driver"
15+
"github.com/LFDT-Panurus/panurus/x/token/services/network/evm/client"
1516
)
1617

1718
var logger = logging.MustGetLogger()
@@ -20,12 +21,14 @@ var logger = logging.MustGetLogger()
2021
// i.e. token.tms.<tms-id>.services.network.evm. Its presence marks a TMS as an EVM network.
2122
const EVMConfigKey = "services.network.evm"
2223

23-
// networkResolver decides whether a (network, channel) pair is served by the EVM driver. It is a
24-
// small seam over the configuration so the driver's routing can be unit-tested without a full
25-
// config service.
24+
// networkResolver decides whether a (network, channel) pair is served by the EVM driver, and yields
25+
// that network's configuration. It is a small seam over the configuration service so the driver's
26+
// routing can be unit-tested without a full config service.
2627
type networkResolver interface {
2728
// IsEVMNetwork reports whether the given network/channel has an EVM network configuration.
2829
IsEVMNetwork(network, channel string) bool
30+
// ConfigFor returns the EVM configuration for the given network/channel.
31+
ConfigFor(network, channel string) (*Config, error)
2932
}
3033

3134
// Driver is the EVM network driver factory. It implements driver.Driver: the network provider calls
@@ -46,13 +49,26 @@ func NewDriver(configService *config.Service) driver.Driver {
4649

4750
// New returns an EVM Network for the given network/channel, or an error if that network is not
4851
// configured for EVM (so the network provider falls through to the next registered driver).
52+
//
53+
// The endorsement service and the submitter are supplied by the SDK wiring, which owns the key
54+
// material and the FSC view manager they need; a Network built here can serve queries and assemble
55+
// approvals once they are injected.
4956
func (d *Driver) New(network, channel string) (driver.Network, error) {
5057
if !d.resolver.IsEVMNetwork(network, channel) {
5158
return nil, errors.Errorf("evm: no evm network configuration for [%s:%s]", network, channel)
5259
}
5360
logger.Debugf("creating evm network [%s:%s]", network, channel)
5461

55-
return newNetwork(network), nil
62+
config, err := d.resolver.ConfigFor(network, channel)
63+
if err != nil {
64+
return nil, err
65+
}
66+
evmClient, err := client.NewJSONRPCClient(config.Endpoint, nil)
67+
if err != nil {
68+
return nil, errors.Wrapf(err, "evm: failed to create a client for [%s:%s]", network, channel)
69+
}
70+
71+
return NewNetwork(network, config, evmClient, nil, nil)
5672
}
5773

5874
// configNetworkResolver resolves EVM networks from the token-sdk configuration.
@@ -78,3 +94,21 @@ func (r *configNetworkResolver) IsEVMNetwork(network, channel string) bool {
7894

7995
return false
8096
}
97+
98+
// ConfigFor loads and validates the EVM configuration of the first TMS declaring it for the given
99+
// network/channel. Every TMS on one EVM network shares the endpoint and chain, so the first match is
100+
// the network's configuration.
101+
func (r *configNetworkResolver) ConfigFor(network, channel string) (*Config, error) {
102+
configs, err := r.cs.Configurations()
103+
if err != nil {
104+
return nil, errors.Wrapf(err, "evm: failed to load token-sdk configurations for [%s:%s]", network, channel)
105+
}
106+
for _, c := range configs {
107+
id := c.ID()
108+
if id.Network == network && id.Channel == channel && c.IsSet(EVMConfigKey) {
109+
return LoadConfig(c)
110+
}
111+
}
112+
113+
return nil, errors.Errorf("evm: no evm network configuration for [%s:%s]", network, channel)
114+
}

x/token/services/network/evm/driver_test.go

Lines changed: 50 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,16 @@ package evm
99
import (
1010
"testing"
1111

12+
"github.com/LFDT-Panurus/panurus/token/services/network/driver"
13+
"github.com/LFDT-Panurus/panurus/x/token/services/network/evm/client/mock"
14+
15+
"github.com/hyperledger-labs/fabric-smart-client/pkg/utils/errors"
1216
"github.com/stretchr/testify/assert"
1317
"github.com/stretchr/testify/require"
1418
)
1519

16-
// fakeResolver reports a fixed set of (network|channel) pairs as EVM networks.
20+
// fakeResolver reports a fixed set of (network|channel) pairs as EVM networks and yields a minimal
21+
// valid configuration for them.
1722
type fakeResolver struct {
1823
evm map[string]bool
1924
}
@@ -22,6 +27,16 @@ func (f fakeResolver) IsEVMNetwork(network, channel string) bool {
2227
return f.evm[network+"|"+channel]
2328
}
2429

30+
func (f fakeResolver) ConfigFor(network, channel string) (*Config, error) {
31+
if !f.IsEVMNetwork(network, channel) {
32+
return nil, errors.Errorf("no evm configuration for [%s:%s]", network, channel)
33+
}
34+
c := validConfig()
35+
c.applyDefaults()
36+
37+
return c, c.Validate()
38+
}
39+
2540
func TestDriverNewRouting(t *testing.T) {
2641
d := &Driver{resolver: fakeResolver{evm: map[string]bool{"evm-net|": true}}}
2742

@@ -41,15 +56,40 @@ func TestDriverNewRouting(t *testing.T) {
4156
assert.Error(t, err)
4257
}
4358

44-
// TestNetworkStubNotImplemented documents that the skeleton's behavioural methods are wired to the
45-
// interface but not yet implemented, so a mis-registration surfaces as a clear error rather than a
46-
// nil-pointer panic.
47-
func TestNetworkStubNotImplemented(t *testing.T) {
48-
n := newNetwork("evm-net")
59+
// TestNetworkSurfaceIsWired checks the methods that used to be stubs now answer through the finality
60+
// manager rather than returning a not-implemented error.
61+
func TestNetworkSurfaceIsWired(t *testing.T) {
62+
evm := &mock.EVMClient{}
63+
// getTokenRequestHash returns the zero hash: the anchor has not been applied.
64+
evm.CallReturns(make([]byte, 32), nil)
65+
n := testNetwork(t, evm, nil)
4966

5067
assert.NotNil(t, n.NewEnvelope())
51-
err := n.Broadcast(t.Context(), &Envelope{})
52-
require.ErrorIs(t, err, errNotImplemented)
53-
_, err = n.Ledger()
54-
assert.ErrorIs(t, err, errNotImplemented)
68+
69+
ledger, err := n.Ledger()
70+
require.NoError(t, err)
71+
require.NotNil(t, ledger)
72+
73+
// An anchor the chain has never seen is Unknown: never the invalid zero code, and never Invalid,
74+
// because a reverted apply is indistinguishable from one still pending (design 7.4).
75+
status, hash, _, err := n.GetTransactionStatus(t.Context(), "token", anchorHex(0x01))
76+
require.NoError(t, err)
77+
assert.Equal(t, driver.Unknown, status)
78+
assert.Nil(t, hash)
79+
80+
code, err := ledger.Status(anchorHex(0x01))
81+
require.NoError(t, err)
82+
assert.Equal(t, driver.Unknown, code)
83+
}
84+
85+
// TestNetworkRejectsMalformedTransactionID checks the anchor-shaped identifier is validated rather
86+
// than silently producing a wrong on-chain lookup.
87+
func TestNetworkRejectsMalformedTransactionID(t *testing.T) {
88+
n := testNetwork(t, nil, nil)
89+
90+
_, _, _, err := n.GetTransactionStatus(t.Context(), "token", "not-a-valid-anchor")
91+
require.Error(t, err)
92+
93+
err = n.AddFinalityListener("token", "not-a-valid-anchor", nil)
94+
require.Error(t, err)
5595
}

0 commit comments

Comments
 (0)