Building RippleNet Topology

Ask any good engineer what goes into writing robust software and they’ll tell you that timely feedback is critically important. Over the years, engineers have developed all kinds of methods to gather feedback on code. From integrated developer env IDEs that provide real-time static analysis, to automated test suites, to code reviews and pair programming, engineers leverage any tools that help ensure code is operating as intended. At Ripple, we use all the standard tools, but it’s not enough.

We are building RippleNet, a global payments network. Over 300 financial institutions around the world install our software on their infrastructure and expect it to interoperate seamlessly with other installs at their partner banks. To accomplish this, we need a way to get feedback on RippleNet and the test frameworks, but today’s out-of-the-box tools just don’t cut it.

RippleNet is nicely modelled as a network topology, in which the vertices of the graph are installs of our software, and the edges are accounting relationships between financial institutions. Imagine a bank in San Francisco running a piece of software that allows it to connect to a bank in New York that is running another copy of the software. The bank in New York may in turn be connected to banks in London or Dubai, creating a global payments network.

Now suppose you’re an engineer who’s just written a new payment feature. Maybe it adds some meta-data to the payment. Maybe it allows customers to leverage a different fee structure. Whatever it is, customers are waiting for it, and you want to ship it. It’s been code reviewed and unit tested, but what you really need is complete confidence that it will work when plugged into the Ripple payment network. To determine this, you need to create a test network.

With the topology library we’ve developed, we can programatically create variations of RippleNet. This allows us to test various network topologies. We have a standard set of regression tests. We have performance tests that report the maximum payment throughput of the network. We can even recreate RippleNet as it exists in production, down to the minor version and database dialect each of our customers use. This is a critical part of our engineering feedback loop. The topology library allows us to iterate on our features, very quickly discovering what does or doesn’t work.

It’s easy to specify a topology. It can be done in Java code or YAML. Start by thinking about what elements you want in your topology. These are things like RippleNet servers, databases, and message queues. Then, think about how you want to configure each element. For example, in order to transact on RippleNet, servers need to be linked together with appropriate accounting relationships between them. This configuration is done by applying the configurers specified in the topology to each element of the topology.

Supposing that we have an XRPLedgerServer and a RippleNetServer which we’d like to add to a topology. For the purpose of testing, we’ll also need to create an AccountLiquidityPathConfigurer which will allow us to configure the accounts on each server when the topology starts. Once we have these, we can incorporate them in a topology, as done below.

Topology testTopology = new Topology()

.addElement(new SpringServerResource("xrp_ledger_server", new XRPLedgerServer())

.addProperty("test_exchange.withdrawal_fee.xrp", "0.05"))

.addElement(new SpringServerResource(“ripple_net_server”, new RippleNetServer())

.addProperty("ripple_net_server.rippleNetAddress", “rn.us.ca”)

.addProperty("db", System.getProperty("db", "h2"))

.addElement(new AccountLiquidityPathConfigurer(

“ripple_net_server”,

“euro_account”,

“usd_account”,

“sending_xrp_account”,

“receiving_xrp_account”,

“eur”,

“usd”,

"test_exchange",

"test_exchange",

LiquidityPathType.INTERNAL));

testTopology.startSync();

Once you start a topology, you’ve got an entire RippleNet under your fingers. The topology API gives a bird’s eye view of the network. You can access elements via simple method calls such as topology.getResource(“ripple_net_server”). This returns the Java client for the server specified as the “ripple_net_server” in our example topology. You can now interact with the REST API. You can execute a payment to another server in the network, and assert that each RippleNet server in the payment chain updated it’s balances appropriately.

The topology library automates the construction of a RippleNet topology, but in order to get feedback on our software, we need to hook into a test framework. We built the topology library to plug into any standard java testing framework.

The recipe goes like this: specify your topology in Java or YAML; load and start the topology in your test suite before hook; run your tests, each accessing any required elements of the topology and exercising the product’s API; destroy the topology in the after hook. It’s a simple but powerful concept. We can now write a set of integration tests for that new payment feature that use it in the context of a realistic network topology. We turned testing from a long and frustrating day spent deploying and configuring servers into a completely automated thirty second task.

A simple example using testNG would look something like this:


public class QuoteTest {

protected static Topology topology;

protected final Logger logger = LoggerFactory.getLogger(getClass());

@BeforeSuite(alwaysRun = true)

public void beforeSuite(){

topology =  TopologyHelper.getTopology();

}

@AfterSuite(alwaysRun = true)

public void afterSuite(){

topology.stopSync();

}

@Test

public void testQuote() {

List<Quote> quotes = topology.getResource(“san_francisco”).getQuotes(“london”);

assertThat(quotes.size(), is(3)); // Expect three quote paths from San Francisco to London

}

RippleNet is a fantastic use case for the topology library. It’s hard to imagine developing RippleNet without it. Any new feature we write is tested within the context of several RippleNet topologies. Without these tests, the feedback loop would be longer, and the process tedious and error prone.

As network usage increases, transaction volume has taken off and the topology has gotten more complex, making the time invested in developing Ripple Topology all the more worthwhile. While we wrote the topology library to help us test RippleNet, we architected it like we do everything we build at Ripple: with an eye for simplicity and extensibility. I expect we’ll find more use cases for it as the product matures.

If you're interested in digging deeper, you can find many more code examples in the open-sourced topology library under Ripple's GitHub organization.