Skip to content

Commit de75352

Browse files
committed
Add subnet routing and source binding
1 parent 6e211bb commit de75352

4 files changed

Lines changed: 64 additions & 9 deletions

File tree

api_sn.go

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ func (self *Api) VerifyKeysSync() (*VerifyKeysResult, error) {
9393

9494
type SnSetWalletArgs struct {
9595
ColdkeySs58 string `json:"coldkey_ss58"`
96+
ClientId *Id `json:"client_id,omitempty"`
9697
}
9798

9899
type SnSetWalletError struct {
@@ -131,6 +132,10 @@ type SnPoolClaimArgs struct {
131132
Epoch int64 `json:"epoch"`
132133
}
133134

135+
type SnPoolClaimError struct {
136+
Message string `json:"message"`
137+
}
138+
134139
// SnPoolClaimResult is a merkle claim against the payout pool.
135140
//
136141
// The epoch/chain/block numbers are int64 rather than uint64 so they bind:
@@ -145,11 +150,15 @@ type SnPoolClaimResult struct {
145150
// Must stay [][]byte: each element marshals to a base64 json string, so
146151
// retyping would change the wire format. Bound via GetProof() below.
147152
//gomobile:noexport [][]byte — bound via GetProof()
148-
Proof [][]byte `json:"proof"`
149-
PayoutRoot []byte `json:"payout_root"`
150-
ContractAddress string `json:"contract_address"`
151-
ChainId int64 `json:"chain_id"`
152-
ClaimOpenBlock int64 `json:"claim_open_block"`
153+
Proof [][]byte `json:"proof"`
154+
PayoutRoot []byte `json:"payout_root"`
155+
ContractAddress string `json:"contract_address"`
156+
ChainId int64 `json:"chain_id"`
157+
ClaimOpenBlock int64 `json:"claim_open_block"`
158+
ArtifactHash string `json:"artifact_hash,omitempty"`
159+
ArtifactUri string `json:"artifact_uri,omitempty"`
160+
SettlementVaultAddress string `json:"settlement_vault_address,omitempty"`
161+
Error *SnPoolClaimError `json:"error,omitempty"`
153162
}
154163

155164
// GetProofLen and GetProofAt are the app-facing accessors for the merkle

api_sn_test.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@ func TestApiSubnetHeadlessBindings(t *testing.T) {
5151
if args.ColdkeySs58 != "coldkey" {
5252
t.Errorf("wallet coldkey = %q, want coldkey", args.ColdkeySs58)
5353
}
54+
if args.ClientId == nil || args.ClientId.IdStr != clientId.IdStr {
55+
t.Errorf("wallet client id = %+v, want %s", args.ClientId, clientId.IdStr)
56+
}
5457
fmt.Fprint(w, `{}`)
5558
case "/sn/pool/claim":
5659
if r.Method != http.MethodGet {
@@ -60,7 +63,7 @@ func TestApiSubnetHeadlessBindings(t *testing.T) {
6063
if got := r.URL.Query().Get("epoch"); got != "42" {
6164
t.Errorf("pool claim epoch = %q, want 42", got)
6265
}
63-
fmt.Fprint(w, `{"epoch":42,"no_id":"AQI=","coldkey":"AwQ=","share_bps":2500,"proof":["BQY="],"payout_root":"Bw==","contract_address":"0xabc","chain_id":9,"claim_open_block":100}`)
66+
fmt.Fprint(w, `{"epoch":42,"no_id":"AQI=","coldkey":"AwQ=","share_bps":2500,"proof":["BQY="],"payout_root":"Bw==","contract_address":"0xabc","chain_id":9,"claim_open_block":100,"artifact_hash":"sha256:0123","artifact_uri":"https://no.example/sn/artifacts/sha256:0123","settlement_vault_address":"0xvault","error":{"message":"claim not open"}}`)
6467
case "/sn/epoch":
6568
if r.Method != http.MethodGet {
6669
t.Errorf("epoch method = %s, want GET", r.Method)
@@ -95,7 +98,7 @@ func TestApiSubnetHeadlessBindings(t *testing.T) {
9598
t.Fatalf("verify keys result = %+v, want decoded key 7", keysResult.Keys)
9699
}
97100

98-
walletResult, err := api.SnSetWalletSyncWithContext(ctx, &SnSetWalletArgs{ColdkeySs58: "coldkey"})
101+
walletResult, err := api.SnSetWalletSyncWithContext(ctx, &SnSetWalletArgs{ColdkeySs58: "coldkey", ClientId: clientId})
99102
if err != nil {
100103
t.Fatal(err)
101104
}
@@ -110,6 +113,12 @@ func TestApiSubnetHeadlessBindings(t *testing.T) {
110113
if claimResult.Epoch != 42 || claimResult.ShareBps != 2500 || claimResult.ContractAddress != "0xabc" || claimResult.ChainId != 9 {
111114
t.Fatalf("pool claim result = %+v", claimResult)
112115
}
116+
if claimResult.ArtifactHash != "sha256:0123" || claimResult.ArtifactUri != "https://no.example/sn/artifacts/sha256:0123" || claimResult.SettlementVaultAddress != "0xvault" {
117+
t.Fatalf("pool claim artifact fields = %+v", claimResult)
118+
}
119+
if claimResult.Error == nil || claimResult.Error.Message != "claim not open" {
120+
t.Fatalf("pool claim error = %+v", claimResult.Error)
121+
}
113122
if string(claimResult.NoId) != string([]byte{1, 2}) || len(claimResult.Proof) != 1 || string(claimResult.Proof[0]) != string([]byte{5, 6}) {
114123
t.Fatalf("pool claim byte fields were not decoded: %+v", claimResult)
115124
}

device_local.go

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -504,6 +504,13 @@ type DeviceLocalSettings struct {
504504
//gomobile:noexport connect.MultiClientIdentityStore is an interface from
505505
// another package, which gomobile does not bind. Go/headless hosts only.
506506
MultiClientIdentityStore connect.MultiClientIdentityStore
507+
// ProviderDialContextSettings, when set, is applied only to the exit NAT's
508+
// TCP and UDP sockets. Headless integration harnesses use it to bind each
509+
// provider to a distinct loopback source address while exercising the real
510+
// tunnel stack on one host. Ordinary applications leave it nil.
511+
//
512+
//gomobile:noexport Go-only network dial seam.
513+
ProviderDialContextSettings *connect.DialContextSettings
507514
// FIXME remove EnableRpc. Turn on RPC when RPC connections are set (receive net.Conn, send net.Conn)
508515
EnableRpc bool
509516
// KeyMaterial, when set, is applied to `ClientSettings` at construction
@@ -3181,9 +3188,19 @@ func (self *DeviceLocal) applyProvideMemorySharesWithLock(provideActive bool) {
31813188
}
31823189
}
31833190

3184-
func providerLocalUserNatSettings(memoryTargetByteCount ByteCount, log connect.Logger) *connect.LocalUserNatSettings {
3191+
func providerLocalUserNatSettings(
3192+
memoryTargetByteCount ByteCount,
3193+
log connect.Logger,
3194+
dialContextSettings ...*connect.DialContextSettings,
3195+
) *connect.LocalUserNatSettings {
31853196
localUserNatSettings := connect.DefaultProviderLocalUserNatSettingsWithMemoryTarget(memoryTargetByteCount)
31863197
localUserNatSettings.Log = log
3198+
if len(dialContextSettings) != 0 && dialContextSettings[0] != nil {
3199+
// Both protocols must expose the same address identity. ICMP uses a
3200+
// platform-specific packet backend and is not involved in /verify.
3201+
localUserNatSettings.TcpBufferSettings.DialContextSettings = dialContextSettings[0]
3202+
localUserNatSettings.UdpBufferSettings.DialContextSettings = dialContextSettings[0]
3203+
}
31873204
return localUserNatSettings
31883205
}
31893206

@@ -3221,7 +3238,11 @@ func (self *DeviceLocal) setProvideModeWithLock(provideMode ProvideMode) (change
32213238
// this avoid connection disruptions
32223239
if self.remoteUserNatProviderLocalUserNat == nil {
32233240
_, _, providerShareByteCount := deviceMemoryShares(self.settings)
3224-
localUserNatSettings := providerLocalUserNatSettings(providerShareByteCount, self.log)
3241+
localUserNatSettings := providerLocalUserNatSettings(
3242+
providerShareByteCount,
3243+
self.log,
3244+
self.settings.ProviderDialContextSettings,
3245+
)
32253246
self.remoteUserNatProviderLocalUserNat = connect.NewLocalUserNat(client.Ctx(), self.clientId.String(), localUserNatSettings)
32263247
}
32273248
if self.remoteUserNatProvider == nil {

device_local_memory_test.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ package sdk
22

33
import (
44
"context"
5+
"errors"
56
"math"
7+
"net"
68
"runtime/debug"
79
"testing"
810

@@ -306,6 +308,20 @@ func TestProviderLocalUserNatSettings(t *testing.T) {
306308
connect.AssertEqual(t, settings.TcpBufferSettings.GlobalLimit, 0)
307309
}
308310

311+
func TestProviderLocalUserNatSettingsAppliesExitDialerOnlyToTCPAndUDP(t *testing.T) {
312+
dial := &connect.DialContextSettings{DialContext: func(context.Context, string, string) (net.Conn, error) {
313+
return nil, errors.New("test dial")
314+
}}
315+
settings := providerLocalUserNatSettings(0, connect.NewNoopLogger(), dial)
316+
if settings.TcpBufferSettings.DialContextSettings != dial || settings.UdpBufferSettings.DialContextSettings != dial {
317+
t.Fatal("provider exit dialer was not applied to TCP and UDP")
318+
}
319+
plain := providerLocalUserNatSettings(0, connect.NewNoopLogger())
320+
if plain.TcpBufferSettings.DialContextSettings != nil || plain.UdpBufferSettings.DialContextSettings != nil {
321+
t.Fatal("ordinary provider settings unexpectedly install a custom dialer")
322+
}
323+
}
324+
309325
// TestDeviceLocalProvideMemoryRealloc verifies the provider share of the
310326
// device memory target follows the provide state: while providing is off it
311327
// backs the client pair; enabling provide moves it to the provider pair and

0 commit comments

Comments
 (0)