Skip to content
Compare
Choose a tag to compare

The latest installment in the Wavelet series. After nearly two months of clean-up and production testing we are proud to release Wavelet v0.2.0.

Based on our internal discussion and community feedback, various important features and fixes were implemented in Wavelet v0.2.0, including transaction fee restructuring and staking rewards, HTTPS support for the API, syncing and voting have been streamlined

New features and major fixes

  • Set the genesis block to the testnet genesis (#249)
  • Add auto-updater (#247)
  • Ignore transaction error caused by adding transactions with too low depth (#243)
  • Change transaction fee and reward calculations (#239)
  • Allow setting log level via CLI parameters (#229)
  • Change vote counting for finalization process (#218)
  • Optimize saving memory of contract snapshot (#202)
  • Apply contracts to VM in same order as they came in (#187)
  • Automatically restart process to recover from various kinds of runtime failures (#184)
  • Memory-efficient syncing (#177)

Transaction Fee Restructuring and Staking Rewards

Prior to Wavelet v0.2.0, transaction fees were a fixed cost and rewards were delivered to a single lucky winner in a round. After a significant amount of analysis we decided that a more fair fee is based on the size of the transaction and should be paid by the creator of the transaction. Additionally, because the current reward scheme encouraged sending low-value transactions to increase the odds that a sender would be a lucky winner, we have changed the reward scheme such that all senders are rewarded proportional to the amount of tokens staked during the round, relative to other senders.

HTTPS Support for the API

Prior to Wavelet v0.2.0 the API only supported HTTP. This had several bad effects for running the Wavelet test network. Many HTTP User Agents required HTTPS to access the API from another HTTPS page, so we added an HTTPS proxy for the test network using CloudFlare, however it did not permit long-lived WebSocket connections. To address this we added HTTPS support directly into the Wavelet core in v0.2.0. It will use Let's Encrypt to automatically fetch an HTTPS certificate and uses the TLS-ALPN-01 challenge so that only a single port needs to be exposed in HTTPS mode, port 443/tcp.

Syncing and Voting have been Streamlined

As discussed in the Wavelet v0.1.1 release notes, syncing support was lacking -- it has been significantly improved and tested. The Wavelet consensus mechanism has also been improved to deal with varying round results due to high traffic and latency. Additionally, many connectivity issues that Wavelet will experience in the real life have been operated on via an extensive global simulator and this has led to many fundamental improvements.

What's coming soon?

  • Bug fixes and cleanup
  • A surprise

A lot of work by the dedicated Wavelet team has gone into this release and I am proud of their effort and results.

Notes

The Windows/amd64 v0.2.0-testnet binary was generated from the v0.2.0 release with an additional patch (3baf2e8) to fix build-time issues on Windows/amd64.

Compare
Choose a tag to compare

After releasing the first beta version of Wavelet one month ago, we have been continuously optimizing both the protocol design and the implementation to make it faster, more secure, and more developer-friendly.

Based on our internal discussion and community feedback, various important features and fixes were implemented in Wavelet v0.1.1, including crowdsourcing gas fees for smart contracts, AssemblyScript smart contract support, CLI autocompletion, and more secure transaction seed rules.

New features and major fixes

  • Implement all that is necessary to enable autocomplete for CLI input across ledger data. (#120)
  • Changed to urfave/cli for the shell, added autocompletion, some 80 col changes. (#121)
  • Ledger state syncing/pull missing transactions - minor cleanup and improvements. (#125)
  • Fixed seed conditions and added tests for applying transactions. (#126)
  • Prefer values over heap-allocated values, simplify memory model, and fix appending parent transaction seeds to transactions. (#132)
  • Crowdsourced gas fees for smart contracts. (#133)
  • Validate WASM code for smart contracts before writing it into account state. (#135)
  • Call the start function of a smart contract (if exists) at initialization. (#145)
  • Improve autocompletion indexing using a radix tree. (#149)
  • Improve nops broadcasting. (#150)
  • Preserve the globals of smart contracts into account state. (#151)

Crowdsourced Gas Fees

When a smart contract is invoked by an on-chain transaction, there's a cost for executing the contract and the cost needs to be paid by some account. The cost is called gas and the virtual currency paid is the gas fee.

Usually, the account that sends the contract-calling transaction has to pay the gas fee. But that's not the only choice to making sure gas fees are covered. For example - a contract's user can participate in crowdsourcing gas fees by transferring money into the contract's gas balance, and legitimate users, once identified, can have their gas fees deducted from the crowdsourced gas balance instead of paying by themselves.

Wavelet v0.1.1 implements crowdsourcing, and we hope the flexible payment method for gas fees can benefit more contract developers and users.

AssemblyScript support

Before v0.1.1, the only smart contract language that we support for Wavelet is Rust. Recently we decided to add official support for more languages, including AssemblyScript.

Rust compiles to WebAssembly through LLVM, while AssemblyScript uses its own code generation backend. So there are quite some differences in the generated code. To support the patterns used by AssemblyScript, we made a few changes to Wavelet's smart contract engine.

Calling start function at initialization

According to the WebAssembly specs, a start function is the "initializer" function that should be called when a module is loaded.

All global variables in Rust are either statically initialized at compilation time or lazily initialized - This means that there's no need for a start function. However, AssemblyScript initializes all non-trivial global variables in the start function. The routines for calling the start function were added to Wavelet in v0.1.1.

Preserving globals

Globals in WebAssembly are similar to global variables in higher-level programming languages. The set of globals and the linear memory compose the persistent state that needs to be preserved across smart contract invocations.

However, only the linear memory was preserved prior to Wavelet v0.1.1. This didn't cause any problem for Rust/LLVM smart contracts because LLVM only uses globals for the linear stack pointer, which does not need to be preserved across invocations. AssemblyScript uses globals also for language-level global variables, so we need to preserve globals too. In v0.1.1, globals are saved under a different key in account state and loaded back at every contract invocation, which fixes the issue.

CLI Autocompletion

There are two ways to interact with a running Wavelet ledger: through the HTTP API, and directly through the command line interface (CLI).

In v0.1.1, we added autocompletion support into the CLI. That means you can enter a prefix of commands, options, and even transaction IDs, and just press TAB to have it autocompleted.

Reducing allocations and expressing immutability in Go

Performance is important for Wavelet. Wavelet is written in Go, a high-level language with a garbage collector and automatic escape analysis, and therefore we need to work with these two language features properly to achieve the best performance.

Meanwhile, syntactical cleanness is also important to ensure that our codebase can be properly maintained. Therefore we pass some objects by value to express "immutability", although Go does not have a good enough optimizer to eliminate all the memory copies.

We did a lot of benchmarks and analysis to balance between performance and cleanness, and the work resulted in PR #132.

Improving the safety of transaction seed rules

As introduced in previous posts, critical transaction is a fundamental part of our consensus protocol. The seed of a transaction is a 256-bit value used to determine the criticalness of a transaction in a similar way as to how PoW hashes work - counting the number of prefix zeros.

Prior to v0.1.1, the seed of a transaction was calculated by:

blake2b_256(
    sender_id +
    concat(sorted_parent_seeds)
)

By adding some additional constraints on the sender ID (minimum stake), this algorithm is resistant to "mining". You can only try new seeds by sending new transactions, for which you need to pay transaction fees.

Although looking good at first glance, this introduces a problem that can be exploited to perform DoS attacks on the network. Once a seed is found, the seed can then be used to create an infinite amount of critical transactions at no cost, as long as they have the same parents.

One possible way to fix the issue is to add a short hash of transaction content into the process of calculating the seed, so the transaction seed is calculated by:

blake2b_256(
    sender_id +
    concat(sorted_parent_seeds) +
    short_hash(transaction_content)
)

This increases the difficulty of reusing a critical seed and is implemented in v0.1.1 with 8-bit short hashes. With a sufficient size for short hashes, the PoW resistance property of transaction seeds is still preserved, and the pressure from critical seed reusing can be offloaded from the consensus protocol in case of malicious activity.

What's coming soon?

  • Full state/transaction syncing support.
  • Account nonces for strict transaction order support and secure forwarding.