Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ All notable changes to this project will be documented in this file.
- Add `IsRetryableFunc` field to `RetryOptions` for configurable retry criteria in the Solana JSON-RPC client; add `"rate limited"` string match and RPC code `-32429` to the default implementation
- Geolocation
- Standardize CLI flag naming: probe mutation commands use `--probe` (was `--code`) accepting pubkey or code; rename `--signing-keypair` → `--signing-pubkey` and `--target-pk` → `--target-signing-pubkey`; add `--json-compact` to `get` commands
- Add optional result destination to `GeolocationUser` so LocationOffsets can be sent to an alternate endpoint instead of the target IP; supports both IP and domain destinations (e.g., `185.199.108.1:9000` or `results.example.com:9000`); includes `SetResultDestination` onchain instruction, CLI `user set-result-destination` command, and Go SDK deserialization (backwards-compatible with existing accounts)
- geoprobe-target can now store LocationOffset messages in ClickHouse
- Add ICMP pinger to geoprobe-agent for measuring outbound ICMP targets with interleaved batch send/receive, integrated into the existing measurement cycle alongside TWAMP
- Remove `--additional-parent`, `--additional-targets`, `--additional-icmp-targets`, and `--allowed-pubkeys` CLI flags from geoprobe-agent; all configuration now comes from onchain state via parent and target discovery
Expand Down
3 changes: 3 additions & 0 deletions client/doublezero-geolocation-cli/src/cli/user.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use doublezero_cli::geolocation::user::{
add_target::AddTargetCliCommand, create::CreateGeolocationUserCliCommand,
delete::DeleteGeolocationUserCliCommand, get::GetGeolocationUserCliCommand,
list::ListGeolocationUserCliCommand, remove_target::RemoveTargetCliCommand,
set_result_destination::SetResultDestinationCliCommand,
update_payment_status::UpdatePaymentStatusCliCommand,
};

Expand All @@ -26,6 +27,8 @@ pub enum UserCommands {
AddTarget(AddTargetCliCommand),
/// Remove a target from a user
RemoveTarget(RemoveTargetCliCommand),
/// Set result destination for geolocation results
SetResultDestination(SetResultDestinationCliCommand),
/// Update payment status (foundation-only)
UpdatePayment(UpdatePaymentStatusCliCommand),
}
1 change: 1 addition & 0 deletions client/doublezero-geolocation-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ fn main() -> eyre::Result<()> {
UserCommands::List(args) => args.execute(&client, &mut handle),
UserCommands::AddTarget(args) => args.execute(&client, &mut handle),
UserCommands::RemoveTarget(args) => args.execute(&client, &mut handle),
UserCommands::SetResultDestination(args) => args.execute(&client, &mut handle),
UserCommands::UpdatePayment(args) => args.execute(&client, &mut handle),
},
Command::InitConfig(args) => args.execute(&client, &mut handle),
Expand Down
25 changes: 17 additions & 8 deletions sdk/geolocation/go/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -327,14 +327,15 @@ type KeyedGeolocationUser struct {
}

type GeolocationUser struct {
AccountType AccountType // 1 byte
Owner solana.PublicKey // 32 bytes
Code string // 4-byte length prefix + UTF-8 bytes
TokenAccount solana.PublicKey // 32 bytes
PaymentStatus GeolocationPaymentStatus // 1 byte
Billing GeolocationBillingConfig // 1 + 16 = 17 bytes
Status GeolocationUserStatus // 1 byte
Targets []GeolocationTarget // 4-byte count + 71*N bytes
AccountType AccountType // 1 byte
Owner solana.PublicKey // 32 bytes
Code string // 4-byte length prefix + UTF-8 bytes
TokenAccount solana.PublicKey // 32 bytes
PaymentStatus GeolocationPaymentStatus // 1 byte
Billing GeolocationBillingConfig // 1 + 16 = 17 bytes
Status GeolocationUserStatus // 1 byte
Targets []GeolocationTarget // 4-byte count + 71*N bytes
ResultDestination string // 4-byte length prefix + UTF-8 bytes (empty = unset)
}

func (g *GeolocationUser) Serialize(w io.Writer) error {
Expand Down Expand Up @@ -369,6 +370,9 @@ func (g *GeolocationUser) Serialize(w io.Writer) error {
return err
}
}
if err := enc.Encode(g.ResultDestination); err != nil {
return err
}
return nil
}

Expand Down Expand Up @@ -446,5 +450,10 @@ func (g *GeolocationUser) Deserialize(data []byte) error {
return err
}
}
// ResultDestination is appended; old accounts without it default to empty string.
if err := dec.Decode(&g.ResultDestination); err != nil {
g.ResultDestination = ""
return nil
}
return nil
}
51 changes: 49 additions & 2 deletions sdk/geolocation/go/state_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ func TestSDK_Geolocation_State_GeolocationUser_RoundTrip(t *testing.T) {
GeoProbePK: solana.NewWallet().PublicKey(),
},
},
ResultDestination: "185.199.108.1:9000",
}

var buf bytes.Buffer
Expand All @@ -208,6 +209,7 @@ func TestSDK_Geolocation_State_GeolocationUser_RoundTrip(t *testing.T) {
require.Len(t, decoded.Targets, 2)
require.Equal(t, original.Targets[0], decoded.Targets[0])
require.Equal(t, original.Targets[1], decoded.Targets[1])
require.Equal(t, original.ResultDestination, decoded.ResultDestination)
}

func TestSDK_Geolocation_State_GeolocationUser_EmptyTargets(t *testing.T) {
Expand All @@ -226,8 +228,9 @@ func TestSDK_Geolocation_State_GeolocationUser_EmptyTargets(t *testing.T) {
LastDeductionDzEpoch: 0,
},
},
Status: geolocation.GeolocationUserStatusSuspended,
Targets: []geolocation.GeolocationTarget{},
Status: geolocation.GeolocationUserStatusSuspended,
Targets: []geolocation.GeolocationTarget{},
ResultDestination: "",
}

var buf bytes.Buffer
Expand All @@ -242,6 +245,50 @@ func TestSDK_Geolocation_State_GeolocationUser_EmptyTargets(t *testing.T) {
require.Equal(t, geolocation.GeolocationPaymentStatusDelinquent, decoded.PaymentStatus)
}

func TestSDK_Geolocation_State_GeolocationUser_BackwardCompat_NoResultDestination(t *testing.T) {
t.Parallel()

original := &geolocation.GeolocationUser{
AccountType: geolocation.AccountTypeGeolocationUser,
Owner: solana.NewWallet().PublicKey(),
Code: "old-user",
TokenAccount: solana.NewWallet().PublicKey(),
PaymentStatus: geolocation.GeolocationPaymentStatusPaid,
Billing: geolocation.GeolocationBillingConfig{
Variant: geolocation.BillingConfigFlatPerEpoch,
FlatPerEpoch: geolocation.FlatPerEpochConfig{
Rate: 1000,
LastDeductionDzEpoch: 42,
},
},
Status: geolocation.GeolocationUserStatusActivated,
Targets: []geolocation.GeolocationTarget{
{
TargetType: geolocation.GeoLocationTargetTypeOutbound,
IPAddress: [4]uint8{8, 8, 8, 8},
LocationOffsetPort: 8923,
TargetPK: solana.PublicKey{},
GeoProbePK: solana.NewWallet().PublicKey(),
},
},
ResultDestination: "",
}

var buf bytes.Buffer
require.NoError(t, original.Serialize(&buf))

// Truncate the trailing 4 bytes (empty Borsh string = 4-byte length prefix)
// to simulate old data without the result_destination field.
data := buf.Bytes()[:buf.Len()-4]

var decoded geolocation.GeolocationUser
require.NoError(t, decoded.Deserialize(data))

require.Equal(t, original.Owner, decoded.Owner)
require.Equal(t, original.Targets[0], decoded.Targets[0])
require.Equal(t, "", decoded.ResultDestination)
}

func TestSDK_Geolocation_State_GeolocationTarget_RoundTrip(t *testing.T) {
t.Parallel()

Expand Down
6 changes: 6 additions & 0 deletions smartcontract/cli/src/geoclicommand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use doublezero_sdk::{
add_target::AddTargetCommand, create::CreateGeolocationUserCommand,
delete::DeleteGeolocationUserCommand, get::GetGeolocationUserCommand,
list::ListGeolocationUserCommand, remove_target::RemoveTargetCommand,
set_result_destination::SetResultDestinationCommand,
update_payment_status::UpdatePaymentStatusCommand,
},
programconfig::init::InitProgramConfigCommand,
Expand Down Expand Up @@ -53,6 +54,7 @@ pub trait GeoCliCommand {
) -> eyre::Result<HashMap<Pubkey, GeolocationUser>>;
fn add_target(&self, cmd: AddTargetCommand) -> eyre::Result<Signature>;
fn remove_target(&self, cmd: RemoveTargetCommand) -> eyre::Result<Signature>;
fn set_result_destination(&self, cmd: SetResultDestinationCommand) -> eyre::Result<Signature>;
fn update_payment_status(&self, cmd: UpdatePaymentStatusCommand) -> eyre::Result<Signature>;

fn resolve_exchange_pk(&self, pubkey_or_code: String) -> eyre::Result<Pubkey>;
Expand Down Expand Up @@ -155,6 +157,10 @@ impl GeoCliCommand for GeoCliCommandImpl<'_> {
cmd.execute(self.client)
}

fn set_result_destination(&self, cmd: SetResultDestinationCommand) -> eyre::Result<Signature> {
cmd.execute(self.client)
}

fn update_payment_status(&self, cmd: UpdatePaymentStatusCommand) -> eyre::Result<Signature> {
cmd.execute(self.client)
}
Expand Down
1 change: 1 addition & 0 deletions smartcontract/cli/src/geolocation/user/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ pub mod delete;
pub mod get;
pub mod list;
pub mod remove_target;
pub mod set_result_destination;
pub mod update_payment_status;
Loading
Loading