When You Gotta Slash Validators...

We have returned once again, good people of the digital Wild West! Lately we’ve been postin’ guides on how to build and implement all sorts of different components fer yer application, and while we gear up fer launch, we’ll continue to bring you step-by-step how-tos so you can introduce different features into whatever it is yer buildin’!

Now while it may not suit every blockchain, slashin’ does provide an additional incentive fer validators to behave well and do their jobs to the best of their ability. This also discourages opportunists and layabouts from gettin’ voted into a forgin’ position and doin’ diddly squat while the network’s integrity is compromised.

You can’t take slashin’ lightly though as it’s basically a form of negative reinforcement, and not all folks respond well to that! That said, if you do want to punish bad validators fer their transgressions, you’ll need some kind of mechanism that detects different kinds of misbehaviors and deals out the relevant punishment - in other words, let the punishment fit the crime!

And so this is what today’s guide is all about - but before we show you how to build and implement a validator slashin’ mechanism, we ask that you review the followin’ terms and definitions so that you know exactly what certain items mean when you encounter ‘em in the code! Here we go!

Let Me Take a Look at That List Again, Amigo

  • @bearmint/bep3 is an NPM package that offers an implementation of BEP-003 which defines a standardized set of assertions that can verify whether certain expectations were fulfilled or not
  • @bearmint/bep13 is an NPM package that offers an implementation of BEP-013 which allows for the sharing of a set of types between all the modules that exist within Bearmint
  • @bearmint/bep16 is an NPM package that offers an implementation of BEP-016 which contains a standardized method of bootstrapping the plugins and application via service providers
  • @bearmint/bep19 is an NPM package that offers an implementation of BEP-019  which explains how protocol buffers will apply in order to provide a standard method of serialization and deserialization
  • @bearmint/bep101 is an NPM package that offers an implementation of BEP-101 which describes an approach to punishing accounts for misbehaving (also referred to as ‘slashing’), and
  • @bearmint/bep108 refers to an NPM package that offers an implementation of BEP-108 which defines the application of Protocol Buffers within Tendermint

Punishing a Validator

Whenever Tendermint tells us about an event or anomaly that qualifies as a misbehavior, it’s important to evaluate this misbehavior (or misbehaviors) so that the relevant punishment can be instituted in accordance with the severity of the misbehavior in question. For this reason, several default checks are in place.

Checking if the Misbehavior Exists

If the type of misbehavior is neither a number nor known by Tendermint, we’ll have to avoid imposing any punishments since no reliable means of determining how we should act is readily available.

if (typeof type !== 'number') {
continue;
}
if (!Object.values(tendermint.abci.EvidenceType).includes(type)) {
continue;
}

Carrying Out the Required Slashing

If the misbehavior is known, we will then slash an account according to the severity of the misbehavior in question. Note that these punishments take the form of percentage-based reductions in stake, so be careful when you configure them.

const account = await state
.getAccounts()
.findByValidatorAddress(arrayBufferToHex(validator.address));
await mergeSlashedStakes({
address: validator.address,
stakes: {
[account.address]: {
amount: await slashAccount({
account,
punishments: getModuleMilestone<SlasherConfiguration>({
handler: '@bearmint/bep102',
state,
}).punishments,
state,
type,
validator: account.validator.name,
}),
},
},
state,
});

Combining the Various Elements

In the second-to-last step, we combine all the different elements to create a slashing mechanism that allows us to impose punishments for different types of misbehaviors.

import { assert } from '@bearmint/bep3';
import { Slasher, SlasherConfiguration, StateStore } from '@bearmint/bep13';
import { arrayBufferToHex } from '@bearmint/bep19';
import { mergeSlashedStakes, slashAccount } from '@bearmint/bep101';
import { tendermint } from '@bearmint/bep108';
function makeValidatorSlasher(): Slasher {
return {
async execute({
misbehaviors,
state,
}: {
misbehaviors: tendermint.abci.IEvidence[];
state: StateStore;
}) {
for (const { type, validator } of misbehaviors) {
if (typeof type !== 'number') {
continue;
}
if (!Object.values(tendermint.abci.EvidenceType).includes(type)) {
continue;
}
assert.defined<Uint8Array>(validator?.address);
const account = await state
.getAccounts()
.findByValidatorAddress(arrayBufferToHex(validator.address));
await mergeSlashedStakes({
address: validator.address,
stakes: {
[account.address]: {
amount: await slashAccount({
account,
punishments: getModuleMilestone<SlasherConfiguration>({
handler: '@bearmint/bep102',
state,
}).punishments,
state,
type,
validator: account.validator.name,
}),
},
},
state,
});
}
},
};
}

Registering With the Application

And finally, we simply bind our newly-created function to the container as ContainerType.ValidatorSlasher - Bearmint will now use our slasher whenever it needs to punish a validator.

import { ContainerType, Cradle, ServiceProvider } from '@bearmint/bep13';
import { makeServiceProviderSkeleton } from '@bearmint/bep16';
import { z } from 'zod';
import { makeValidatorSlasher } from './slasher';
function makeServiceProvider(cradle: Cradle): ServiceProvider {
const base = makeServiceProviderSkeleton(__dirname);
return {
...base,
configSchema() {
return z.object({
punishments: z.object({
duplicateVote: z.number(),
lightClientAttack: z.number(),
unknown: z.number(),
}),
});
},
async register() {
cradle.Container.bind(ContainerType.ValidatorSlasher).toFunctionSingleton(
makeValidatorSlasher,
);
},
};
}

That’s It! Done and Dusted!

We’ve come to the end of yet another guide friends. We trust that wasn’t too confusin’ or anythin’ - however, if this really isn’t what yer lookin’ fer, or if you need somethin’ different fer yer application, then feel free to get in touch with us so we can assist you. We’re always lookin’ fer ways to help folks out and are always up for a challenge. Rest assured, if you work with us and trust ol’ Buckley, then there ain’t nothin’ that can’t be done!