Skip to content

Commit 456516a

Browse files
authored
update: update to latest versions of rippled/witness server (#112)
## High Level Overview of Change Title says it all. ### Context of Change rippled was modified to only allow non-batch attestations. ### Type of Change - [x] Bug fix (non-breaking change which fixes an issue) - [x] New feature (non-breaking change which adds functionality) ## Test Plan Tests pass locally. I can't run CI yet because the docker containers haven't been updated.
1 parent cdf2070 commit 456516a

File tree

9 files changed

+734
-594
lines changed

9 files changed

+734
-594
lines changed

poetry.lock

Lines changed: 639 additions & 504 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ websockets = "^10.3"
3030
Jinja2 = "^3.1.2"
3131
psutil = "^5.9.2"
3232
docker = "^6.0.0"
33-
xrpl-py = "^1.8.0b0"
33+
xrpl-py = { git = "https://github.com/XRPLF/xrpl-py.git", rev = "sidechain-2.5" }
34+
pycryptodome = "^3.17"
3435

3536
[tool.poetry.dev-dependencies]
3637
flake8 = "^5.0.4"

sidechain_cli/bridge/setup.py

Lines changed: 47 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import json
44
import os
55
from pprint import pformat
6-
from typing import Any, Dict, List, Optional, Tuple
6+
from typing import Any, Dict, List, Literal, Optional, TypedDict, Union, cast
77

88
import click
99
from xrpl import CryptoAlgorithm
@@ -15,6 +15,7 @@
1515
XRP,
1616
AccountSet,
1717
AccountSetFlag,
18+
Amount,
1819
Currency,
1920
IssuedCurrency,
2021
ServerState,
@@ -23,15 +24,11 @@
2324
Transaction,
2425
TrustSet,
2526
XChainAccountCreateCommit,
26-
XChainAddAttestation,
27+
XChainAddAccountCreateAttestation,
2728
XChainBridge,
2829
XChainCreateBridge,
2930
)
3031
from xrpl.models.transactions.transaction import transaction_json_to_binary_codec_form
31-
from xrpl.models.transactions.xchain_add_attestation import (
32-
XChainAttestationBatch,
33-
XChainCreateAccountAttestationBatchElement,
34-
)
3532
from xrpl.wallet import Wallet
3633

3734
from sidechain_cli.exceptions import SidechainCLIException
@@ -43,8 +40,25 @@
4340
submit_tx,
4441
)
4542

46-
_ATTESTATION_ENCODE_ORDER: List[Tuple[str, int]] = [
47-
("account", 4),
43+
44+
class _UnsignedAttestation(TypedDict):
45+
xchain_bridge: XChainBridge
46+
other_chain_source: str
47+
amount: Amount
48+
attestation_reward_account: str
49+
was_locking_chain_send: Union[Literal[0], Literal[1]]
50+
xchain_account_create_count: str
51+
destination: str
52+
signature_reward: Amount
53+
54+
55+
class _SignedAttestation(_UnsignedAttestation):
56+
public_key: str
57+
signature: str
58+
59+
60+
_ATTESTATION_ENCODE_ORDER = [
61+
("other_chain_source", 6),
4862
("amount", 2),
4963
("signature_reward", 4),
5064
("attestation_reward_account", 6),
@@ -55,29 +69,30 @@
5569

5670

5771
def _sign_attestation(
58-
attestation: XChainCreateAccountAttestationBatchElement,
72+
attestation: _UnsignedAttestation,
5973
bridge: XChainBridge,
60-
private_key: str,
61-
) -> XChainCreateAccountAttestationBatchElement:
62-
attestation_dict = attestation.to_dict()[
63-
"xchain_create_account_attestation_batch_element"
64-
]
74+
wallet: Wallet,
75+
) -> _SignedAttestation:
6576
# TODO: use this instead once it's been implemented
66-
# attestation_xrpl = transaction_json_to_binary_codec_form(attestation_dict)
77+
# attestation_xrpl = transaction_json_to_binary_codec_form(attestation)
6778
# encoded_obj = encode(attestation_xrpl)
6879
bridge_dict: Dict[str, Any] = {"xchain_bridge": bridge.to_dict()}
6980
encoded_obj = encode(transaction_json_to_binary_codec_form(bridge_dict))[4:]
7081
for key, prefix in _ATTESTATION_ENCODE_ORDER:
71-
value = attestation_dict[key]
82+
value = attestation[key] # type: ignore # Hard to type
7283
if key == "was_locking_chain_send":
7384
encoded_obj += "0" + str(value)
7485
else:
7586
xrpl_attestation = transaction_json_to_binary_codec_form({key: value})
7687
encoded_obj += encode(xrpl_attestation)[prefix:]
77-
signature = sign(bytes.fromhex(encoded_obj), private_key)
78-
attestation_dict["signature"] = signature
79-
signed_attestation = XChainCreateAccountAttestationBatchElement.from_dict(
80-
attestation_dict
88+
signature = sign(bytes.fromhex(encoded_obj), wallet.private_key)
89+
signed_attestation: _SignedAttestation = cast(
90+
_SignedAttestation,
91+
{
92+
**attestation,
93+
"signature": signature,
94+
"public_key": wallet.public_key,
95+
},
8196
)
8297
return signed_attestation
8398

@@ -342,16 +357,14 @@ def setup_bridge(
342357
# we need to create the witness reward + submission accounts via the bridge
343358

344359
# helper function for submitting the attestations
345-
def _submit_attestations(
346-
attestations: List[XChainCreateAccountAttestationBatchElement],
360+
def _submit_attestation(
361+
attestation: _SignedAttestation,
347362
) -> None:
348-
attestation_tx = XChainAddAttestation(
349-
account=issuing_door,
350-
xchain_attestation_batch=XChainAttestationBatch(
351-
xchain_bridge=bridge_obj,
352-
xchain_claim_attestation_batch=[],
353-
xchain_create_account_attestation_batch=attestations,
354-
),
363+
attestation_tx = XChainAddAccountCreateAttestation.from_dict(
364+
{
365+
**attestation,
366+
"account": issuing_door,
367+
}
355368
)
356369
submit_tx(
357370
attestation_tx,
@@ -368,7 +381,6 @@ def _submit_attestations(
368381
funding_wallet = Wallet(funding_seed, 0, algorithm=funding_wallet_algo)
369382

370383
amount = str(min_create2 * 2) # submit accounts need spare funds
371-
attestations = []
372384
count = 1
373385

374386
# create the accounts
@@ -388,30 +400,22 @@ def _submit_attestations(
388400

389401
# set up the attestations for the commit
390402
for account in accounts_issuing_check:
391-
init_attestation = XChainCreateAccountAttestationBatchElement(
392-
account=funding_wallet.classic_address,
403+
init_attestation = _UnsignedAttestation(
404+
xchain_bridge=bridge_obj,
405+
other_chain_source=funding_wallet.classic_address,
393406
amount=amount,
394407
attestation_reward_account=issuing_door,
395408
destination=account,
396-
public_key=issuing_door_wallet.public_key,
397-
signature="",
398409
signature_reward=signature_reward,
399410
was_locking_chain_send=1,
400411
xchain_account_create_count=str(count),
401412
)
402413
signed_attestation = _sign_attestation(
403-
init_attestation, bridge_obj, issuing_door_wallet.private_key
414+
init_attestation, bridge_obj, issuing_door_wallet
404415
)
405-
attestations.append(signed_attestation)
416+
_submit_attestation(signed_attestation)
406417
count += 1
407418

408-
# we can only have 8 attestations in an XChainAddAttestation tx
409-
if len(attestations) == 8:
410-
_submit_attestations(attestations)
411-
attestations = []
412-
413-
_submit_attestations(attestations)
414-
415419
# set up multisign on the door account
416420
signer_tx2 = SignerListSet(
417421
account=issuing_door,

sidechain_cli/bridge/transfer.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -194,9 +194,11 @@ def send_transfer(
194194
node["CreatedNode"] for node in nodes if "CreatedNode" in node.keys()
195195
]
196196
claim_ids_ledger_entries = [
197-
node for node in created_nodes if node["LedgerEntryType"] == "XChainClaimID"
197+
node
198+
for node in created_nodes
199+
if node["LedgerEntryType"] == "XChainOwnedClaimID"
198200
]
199-
assert len(claim_ids_ledger_entries) == 1
201+
assert len(claim_ids_ledger_entries) == 1, len(claim_ids_ledger_entries)
200202
xchain_claim_id = claim_ids_ledger_entries[0]["NewFields"]["XChainClaimID"]
201203

202204
# XChainTransfer

sidechain_cli/server/start.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@
99
from typing import List, Tuple
1010

1111
import click
12+
import docker
1213
import httpx
1314

14-
import docker
1515
from sidechain_cli.exceptions import SidechainCLIException
1616
from sidechain_cli.utils import (
1717
ChainData,
@@ -29,7 +29,7 @@
2929
"..",
3030
"..",
3131
"..",
32-
"docker",
32+
"docker-setup",
3333
"docker-compose.yml",
3434
)
3535
)
@@ -103,7 +103,7 @@ def _run_process(
103103
"--exe",
104104
required=True,
105105
prompt=True,
106-
type=click.Path(exists=True),
106+
type=str, # TODO: should be Union[Literal["docker"], click.Path(exists=True)]
107107
help="The filepath to the executable.",
108108
)
109109
@click.option(
@@ -227,15 +227,15 @@ def start_server(
227227
envvar="RIPPLED_EXE",
228228
required=True,
229229
prompt=True,
230-
type=click.Path(exists=True),
230+
type=str, # TODO: should be Union[Literal["docker"], click.Path(exists=True)]
231231
help="The filepath to the rippled executable.",
232232
)
233233
@click.option(
234234
"--witnessd_exe",
235235
envvar="WITNESSD_EXE",
236236
required=True,
237237
prompt=True,
238-
type=click.Path(exists=True),
238+
type=str, # TODO: should be Union[Literal["docker"], click.Path(exists=True)]
239239
help="The filepath to the witnessd executable.",
240240
)
241241
@click.option("--docker", is_flag=True, help="Use executables from Docker.")

sidechain_cli/utils/attestations.py

Lines changed: 31 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,9 @@ def wait_for_attestations(
6363
transfer_amount = amount.to_dict()
6464

6565
if is_transfer:
66-
batch_name = "XChainClaimAttestationBatch"
66+
tx_type = "XChainAddClaimAttestation"
6767
else:
68-
batch_name = "XChainCreateAccountAttestationBatch"
68+
tx_type = "XChainAddAccountCreateAttestation"
6969

7070
if close_ledgers:
7171
wait_time = _WAIT_STEP_LENGTH
@@ -82,55 +82,48 @@ def wait_for_attestations(
8282
while True:
8383
time.sleep(wait_time)
8484
if close_ledgers:
85-
ledger = to_client.request(
86-
Ledger(ledger_index="current", transactions=True, expand=True)
87-
)
88-
else:
89-
ledger = to_client.request(
90-
Ledger(ledger_index="validated", transactions=True, expand=True)
91-
)
85+
to_client.request(GenericRequest(method="ledger_accept"))
86+
ledger = to_client.request(
87+
Ledger(ledger_index="validated", transactions=True, expand=True)
88+
)
9289

9390
new_txs = ledger.result["ledger"]["transactions"]
9491
for tx in new_txs:
95-
if tx["TransactionType"] == "XChainAddAttestation":
96-
batch = tx["XChainAttestationBatch"]
97-
if batch["XChainBridge"] != bridge_config.to_xrpl():
92+
if tx["TransactionType"] == tx_type:
93+
if tx["XChainBridge"] != bridge_config.to_xrpl():
9894
# make sure attestation is for this bridge
9995
continue
100-
attestations = batch[batch_name]
101-
for attestation in attestations:
102-
element = attestation[f"{batch_name}Element"]
103-
# check that the attestation actually matches this transfer
104-
if element["Account"] != from_wallet.classic_address:
105-
continue
106-
if element["Amount"] != transfer_amount:
107-
continue
108-
if element["Destination"] != to_account:
109-
continue
110-
if is_transfer:
111-
if element["XChainClaimID"] != xchain_claim_id:
112-
continue
113-
if element["PublicKey"] in attestations_seen:
114-
# already seen this attestation, skip
96+
# check that the attestation actually matches this transfer
97+
if tx["OtherChainSource"] != from_wallet.classic_address:
98+
continue
99+
if tx["Amount"] != transfer_amount:
100+
continue
101+
if tx["Destination"] != to_account:
102+
continue
103+
if is_transfer:
104+
if tx["XChainClaimID"] != xchain_claim_id:
115105
continue
116-
attestations_seen.add(element["PublicKey"])
117-
if verbose > 1:
118-
click.echo(pformat(element))
119-
if verbose > 0:
120-
click.secho(
121-
f"Received {len(attestations_seen)} attestations",
122-
fg="bright_green",
123-
)
106+
if tx["PublicKey"] in attestations_seen:
107+
# already seen this attestation, skip
108+
continue
109+
attestations_seen.add(tx["PublicKey"])
110+
if verbose > 1:
111+
click.echo(pformat(tx))
112+
if verbose > 0:
113+
click.secho(
114+
f"Received {len(attestations_seen)} attestations",
115+
fg="bright_green",
116+
)
124117
if len(new_txs) > 0: # TODO: only count attestations
125118
time_count = 0
126119
else:
127120
time_count += wait_time
128-
if close_ledgers:
129-
to_client.request(GenericRequest(method="ledger_accept"))
121+
130122
if len(attestations_seen) >= bridge_config.quorum:
131123
# received enough attestations for quorum
132124
break
133125

134126
if time_count > attestation_time_limit:
135-
print(pformat(to_client.request(LedgerData()).result))
127+
if verbose >= 2:
128+
click.echo(pformat(to_client.request(LedgerData()).result))
136129
raise AttestationTimeoutException()

tests/conftest.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ def pytest_configure(config):
3232
global mocked_home_dir, config_dir, mocked_vars
3333
runner = CliRunner()
3434
runner.invoke(main, ["server", "stop", "--all"])
35+
if os.getenv("RIPPLED_EXE") is None:
36+
raise Exception("Environment variable `RIPPLED_EXE` is not defined.")
37+
if os.getenv("WITNESSD_EXE") is None:
38+
raise Exception("Environment variable `WITNESSD_EXE` is not defined.")
3539

3640
if os.getenv("CI") != "True":
3741
config_dir = tempfile.TemporaryDirectory()

tests/server/test_server.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@
55
import subprocess
66
import time
77

8+
import docker
89
import psutil
910
import pytest
1011

11-
import docker
1212
from sidechain_cli.main import main
1313
from sidechain_cli.server.start import _DOCKER_COMPOSE
1414
from sidechain_cli.utils import get_config
@@ -98,6 +98,7 @@ def test_list_dead_process(self, runner):
9898
else:
9999
witness = get_config().get_witness(process_to_kill)
100100
os.kill(witness.pid, signal.SIGINT)
101+
time.sleep(0.01)
101102

102103
process = psutil.Process(pid=witness.pid)
103104
assert (

0 commit comments

Comments
 (0)