As a proof-of-stake network, 33% of the stake can censor the network, therefore the minimum number of validators with stake adding up to 33% could collude to censor the network. The Stake-o-Matic is a program that automatically delegates stake according to predefined rules. The idea is that if validators write their own versions (according to their preferences), and staking via their Stake-o-Matic is incentivised, the global stake will decentralise across many validators. I have no idea if that will actually happen, but I’ve been playing with the Stake-o-Matic reference implementation.

Running the Reference Implementation

Clone and build the Solana Stake-o-Matic from the github:

git clone https://github.com/solana-labs/solana.git
cd solana/stake-o-matic/
cargo build
cd ../target/debug
./solana-stake-o-matic
error: The following required arguments were not provided:
<ADDRESS>
<KEYPAIR>
--validator-list <FILE>
USAGE:
solana-stake-o-matic <ADDRESS> <KEYPAIR> --baseline-stake-amount <SOL> --bonus-stake-amount <SOL> --config <PATH> --quality-block-producer-percentage <PERCENTAGE> --validator-list <FILE>
$ solana-keygen new -o stake_authority.json$ solana-keygen pubkey stake_authority.json
7P5z1mfoDQYEwMr1mSKCsDM31pid9B7u26Kx7jRdoMaU
# ask in discord if you don't have
solana pay 7P5z1mfoDQYEwMr1mSKCsDM31pid9B7u26Kx7jRdoMaU 10000
$ solana balance 7P5z1mfoDQYEwMr1mSKCsDM31pid9B7u26Kx7jRdoMaU
10000 SOL
$ solana-keygen new -o stake_account.json$ solana-keygen pubkey stake_account.json
4AkWp8KbDBX6Pyo95zgyuW5e7rpERS1n2jBwC2Xzumbe
$ solana create-stake-account --from stake_authority.json stake_account.json 8000$ solana stake-account 4AkWp8KbDBX6Pyo95zgyuW5e7rpERS1n2jBwC2Xzumbe
Balance: 8000 SOL
Rent Exempt Reserve: 0.00228288 SOL
Stake account is undelegated
Stake Authority: 7P5z1mfoDQYEwMr1mSKCsDM31pid9B7u26Kx7jRdoMaU
Withdraw Authority: 7P5z1mfoDQYEwMr1mSKCsDM31pid9B7u26Kx7jRdoMaU
./target/debug/solana-stake-o-matic 
--cluster testnet
--quality-block-producer-percentage 10
--baseline-stake-amount 1
--bonus-stake-amount 5
4AkWp8KbDBX6Pyo95zgyuW5e7rpERS1n2jBwC2Xzumbe
stake_authority.json
--confirm
solana stakes > x.x
Stake Pubkey: jjShyLqFt3cf29ZQqwYLYuizik2PtgV4VfEAn6WvVKm
Balance: 5 SOL
Rent Exempt Reserve: 0.00228288 SOL
Delegated Stake: 4.99771712 SOL
Active Stake: 0 SOL
Activating Stake: 4.99771712 SOL
Stake activates starting from epoch: 99
Delegated Vote Account Address: 4oUQ7ywb1ahguAyWaP9DiWgcg1BWSWmD4wjF7KkH5Bke
Stake Authority: 7P5z1mfoDQYEwMr1mSKCsDM31pid9B7u26Kx7jRdoMaU
Withdraw Authority: 7P5z1mfoDQYEwMr1mSKCsDM31pid9B7u26Kx7jRdoMaU
$ solana validators | grep 4oUQ7ywb1ahguAyWaP9DiWgcg1BWSWmD4wjF7KkH5Bke
B4xFUYq2TDmz6PsiCj29vFyvmYvqTRAtnhQ3uSmxQVFd 4oUQ7ywb1ahguAyWaP9DiWgcg1BWSWmD4wjF7KkH5Bke 100% 37289879 37289817 2518891 1.3.11 f107b9b4 16788.555581984 SOL (0.32%)

Overview of the Reference Implementation

The program does a bunch of things, such as making a lists of quality and poor block producers, making large numbers of transactions, checking the confirmations. But the heart of what is happening can be found in the main() function. For every vote_account on the validator list, two addresses are determined, a baseline and a bonus. These are derived deterministically relative to the authorized_staker plus a seed. This means a particular authorized_staker and vote_account, will always map to the baseline and bonus addresses (seed differentiates baseline from bonus). Here is the code for the baseline address:

let baseline_stake_address = Pubkey::create_with_seed(
&config.authorized_staker.pubkey(),
baseline_seed,
&solana_stake_program::id(),
)
Transaction::new_unsigned(Message::new(
&stake_instruction::split_with_seed(
&config.source_stake_address, // funds from here
&config.authorized_staker.pubkey(), // auth on the above sacc
config.baseline_stake_amount,
&baseline_stake_address, // derived from:
&config.authorized_staker.pubkey(), // base
baseline_seed, // seed
),
Some(&config.authorized_staker.pubkey()), // signer
)),
$ solana stake-account CibxRr1fG9GbVjkS4FVBU1EdLpQQmor49TARxnE9ep9o
Balance: 0.05 SOL
Rent Exempt Reserve: 0.00228288 SOL
Stake account is undelegated
Stake Authority: Bc3v8aHmfBCwcZqGUMUrfAoW9Cd4hf6Ai3iASfRrUW6P
Withdraw Authority: Bc3v8aHmfBCwcZqGUMUrfAoW9Cd4hf6Ai3iASfRrUW6P
Transaction::new_unsigned(Message::new(
&[stake_instruction::delegate_stake(
&baseline_stake_address,
&config.authorized_staker.pubkey(),
&vote_pubkey,
)],
Some(&config.authorized_staker.pubkey()),
)),
Transaction::new_unsigned(Message::new(
&[stake_instruction::deactivate_stake(
&baseline_stake_address,
&config.authorized_staker.pubkey(),
)],
Some(&config.authorized_staker.pubkey()),
)),

Tweaking the Quality Criteria

An easy way to tweak the program is not to change it at all but just pass in a modified list, the -cluster argument, to which we passed testnet uses a pre-defined list in validators_list.rs, but we can also pass in a customised yaml list.

$ solana gossip -v | tee | head -n 5
IP Address | Node identifier | Gossip | TPU | RPC | Version
----------------+----------------------------------------------+--------+-------+-------+----------------
216.24.140.155 | 5D1fNXzvv5NjV1ysLjirC4WY92RNsVH18vjmcszZd8on | 8001 | 8004 | 8899 | 1.3.13 838aaee1
$ whois -h bgp.tools " -v 216.24.140.155"
AS | IP | BGP Prefix | CC | Registry | Allocated | AS Name
13649 | 216.24.140.155 | 216.24.128.0/19 | US | ARIN | 1998-07-23 | ViaWest
No more than 3 nodes at any location
$ solana gossip -v | ./vdump.js -max 3 -oi > validator.list
$ cat validator.list | wc -l
37
$ solana-keygen new -o location_stake_authority.json
pubkey: ADM36iN9bNGusB6xbRdWbbDPsVAvJYDDeRCszqZAcVKp
$ solana pay ADM36iN9bNGusB6xbRdWbbDPsVAvJYDDeRCszqZAcVKp 37010$ solana balance ADM36iN9bNGusB6xbRdWbbDPsVAvJYDDeRCszqZAcVKp
37010 SOL
$ solana-keygen new -o location_stake_account.json
$ solana create-stake-account --from location_stake_authority.json location_stake_account.json 37000
./target/debug/solana-stake-o-matic --validator-list ./validator.list --baseline-stake-amount 100 --bonus-stake-amount 900 ./location_stake_account.json ./location_stake_authority.json
let too_many_poor_block_producers = false;
./target/debug/solana-stake-o-matic --validator-list ./validator.list --baseline-stake-amount 100 --bonus-stake-amount 900 ./location_stake_account.json ./location_stake_authority.json --confirm
solana-keygen pubkey ./location_stake_account.json 
BtL7aXQaoqB8oL9o7JYanBXrWduttCzM7UEnafM9EAp4
$ solana stake-account BtL7aXQaoqB8oL9o7JYanBXrWduttCzM7UEnafM9EAp4
Balance: 3000 SOL
Rent Exempt Reserve: 0.00228288 SOL
Stake account is undelegated
Stake Authority: ADM36iN9bNGusB6xbRdWbbDPsVAvJYDDeRCszqZAcVKp
Withdraw Authority: ADM36iN9bNGusB6xbRdWbbDPsVAvJYDDeRCszqZAcVKp
$ solana stakes > x.x
$ cat x.x | grep ADM36iN9bNGusB6xbRdWbbDPsVAvJYDDeRCszqZAcVKp | grep Stake | wc -l
69
solana stakes > x.x
cat x.x | sed 's/^$/XXXX/' | tr '\r\n' ' ' | sed 's/XXXX/\n/g' | grep ADM36iN9bNGusB6xbRdWbbDPsVAvJYDDeRCszqZAcVKp > our.splits
$ cat our.splits | grep "100 SOL" | wc -l
34
$ cat our.splits | grep "900 SOL" | wc -l
34
$ solana validators > all.validators
$ cat validator.list | sed 's/- //' > mod_validator.list
$ while read p; do grep "$p" all.validators; done < mod_validator.list | egrep '^!'
! 8xFQP5mPt9Nzr5wMeUsH4azMM3zyGqrUn3LeopKSwptX GT7CUaRjQ3HDuVfqXeA7Cv3w9kWWi9HvAo9HXHvHuZfQ 100% 37725186 37725106 2898035 1.3.13 4265.466981191 SOL (0.07%)
! 6iVVocmqzwe32zGQJ6Yp9KR6FCByA1enPKd7PbS3mAZ4 25x7uZMYEZgJvNKDTzqz1KYEX7HamPCHgsrKyEztW4oo 100% 36683980 36683937 0 1.3.11 132.339117029 SOL (0.00%)
! ChorusM5BVgnAKbg9PF15285LkqeCoZWK2p9s35T7J2A FUdmzVtQ4UEq1TcGRQDy81KM4nfaNNCKaKhCZNeWPveE 100% 37687867 37687811 2913729 1.3.11 10365.822525841 SOL (0.17%)
55 14 * * * cd /home/smith/SOM/solana ; ./target/debug/solana-stake-o-matic --validator-list ./validator.list --baseline-stake-amount 100 --bonus-stake-amount 900 ./location_stake_account.json ./location_stake_authority.json --confirm > som.log 2>&1

Thoughts on the Reference Implementation

One thing that I noticed was that there is no withdrawing and redistributing of the undelegated accounts.