Welcome to Plasma Group¶
Hello and welcome to Plasma Group! Weâre building free and open software to scale blockchains right now. Right now weâre mainly focused on building an easy to use general purpose plasma chain.
Just like any big software endeavor, the Plasma Group ecosystem is composed of a lot of moving parts. In an effort to keep this ecosystem maintainable, weâre documenting as much as we possibly can. A lot of our documentation is about Plasma Group projects, but thereâs also a lot about plasma, layer 2, and blockchains in general.
Understanding: Plasma Chains vs. Sidechains¶
People often talk about plasma chains and sidechains like theyâre the same thing. Sometimes people even refer to plasma chains as sidechains. However, plasma chains and sidechains are very different! Itâs really important to understand these differences because plasma chains and sidechains make different promises about the safety of your funds.
Sidechains¶
The Basics¶
The idea of the âsidechainâ was first popularized by this paper published in 2014. First applied mainly to Bitcoin, the sidechain concept was basically to run another blockchain alongside some other âmainâ blockchain. These two blockchains could then talk to each other in a special way that made it possible for assets to move between the two chains.
Letâs take a look at how this might look in the world of Ethereum. If we want to create an Ethereum sidechain, we first have to create another blockchain. Weâre going to create an Ethereum clone for the sake of this thought experiment.
Setting up an Ethereum clone is really simple. Youâd just need to run any standard Ethereum client (like Parity or Geth) and set it up to create a new blockchain instead of connecting to an existing one. Youâd also need a âconsensus mechanism,â which basically just means you need a way to create new blocks. In theory you could use Proof-of-Work, the same system Ethereum uses, but for now letâs just assign ourselves the sole power to create blocks (basically âProof-of-Authorityâ).
Now youâd just need some way for assets to move between the two blockchains. Usually, this is done by creating a smart contract on Ethereum. When users want to move assets from Ethereum onto your sidechain, they deposit those assets into a smart contract sitting on Ethereum. Youâd watch for these deposits on Ethereum and re-create those assets on your sidechain. Similarily, when users want to move assets from your sidechain back onto Ethereum, you delete those assets from your sidechain and allow the user to unlock the asset again on Ethereum. Itâs really as simple as that!
The Pros¶
If you think about what we just described, thereâs really no reason why the person who originally deposited some asset also has to be the same person to withdraw the asset. This is what makes sidechains so cool â assets can be moved around a lot before theyâre withdrawn. Even though we mightâve made dozens of transactions on the sidechain, only two transactions (the deposit and the withdrawal) ever occur on Ethereum. Since transactions on the sidechain are almost always cheaper than transactions on Ethereum, we get scalability!
The Cons¶
If you think about the thing we just described, you might see some flaws. Remember that we gave you the sole power to create new blocks. What happens if you stop producing blocks altogether? Or even worse, what happens if you stop allowing anyone to withdraw funds from the sidechain?
Itâs completely possible for you to do both of these things. Usually this is somewhat mitigated by creating a sidechain with a more robust consensus mechanism. For example, you could copy Ethereumâs Proof-of-Work.
Unfortunately this still doesnât fix all of the problems with sidechains. Thereâs a reason why transactions on the sidechain are cheaper than transactions on Ethereum. When youâre paying fees on a blockchain, youâre paying the miners who keep the blockchain secure. Generally speaking, the more you pay in fees, the more security you get. If the sidechain had just as much hash power as Ethereum (so the same level of security), transactions on the sidechain would cost pretty much the same as transactions on Ethereum.
All of this means that, in general, if a sidechain is cheaper than Ethereum then itâs going to be (proportionally) less secure than Ethereum. If the sidechain fails (meaning the consensus mechanism gets compromised), you could lose all of your funds. So itâs all about the amount of risk youâre willing to take. You might feel comfortable putting 1 ETH on a sidechain but not 100 ETH.
Plasma Chains¶
The Basics¶
Plasma chains were popularized by the plasma paper published in 2016. In a nutshell, plasma chains are sort of like sidechains, except they trade off some utility for extra security.
Just like sidechains, plasma chains have a consensus mechanism that creates blocks. However, unlike sidechains, the ârootâ of each plasma chain block is published to Ethereum. Block ârootsâ are basically little pieces of information that users can use to prove things about the contents of those blocks. For example, a user could use a block root to prove that they made a transaction in that specific block.
Todo
Write an article about how users can use a block root to prove things about the contents of that block.
The Pros¶
Plasma chain block roots act sort of like âsave pointsâ in the blockchain. Remember that one of the major cons of sidechains is that sidechain consensus mechanisms can stop producing blocks and lock everyoneâs funds up forever. Since itâs possible for users to use block roots to show that they received funds on the plasma chain, plasma doesnât have this problem! If the plasma chain consensus mechanism stops creating blocks, users can use the block roots to make claims to Ethereum (âI claim I had 10 ETH on the plasma chain and I want to withdraw it.â).
Effectively, this means that plasma chains are safer than sidechains by design. Your funds are only ever at risk if Ethereum fails, but you probably have bigger problems. Simply stated, a plasma chain is as secure as the main chain consensus mechanism, whereas as sidechain is only as secure as its own consensus mechanism. This convenient property also means that the plasma chain can use really simple consensus mechanisms (like just a single authority!) and still be safe.
Todo
Link out to the operator explainer page.
The Cons¶
So plasma chains give us cheap transactions that are as secure as the main blockchain. But whatâs the catch?
Well, when weâre using a sidechain we have to trust the sidechain consensus mechanism. If that mechanism fails, weâre out of luck anyway. That trust makes it possible to do really complex things because we also implicitly trust that the sidechain will be around in the future.
On a plasma chain, we keep funds more secure by not making that assumption. We always have to assume that the plasma chain consensus mechanism could fail at any moment and need to design around that. This adds extra restrictions to the things that are possible on a plasma chain.
Take, for example, a very long (letâs say 1 year) timelock contract. You could definitely put that contract on a sidechain if you trust that the sidechain will be around in a year. But since we donât trust that the plasma chain will be around in year (even if itâs the exact same consensus mechanism!), we need to think a little bit outside of the box. We basically need to make sure that if the consensus mechanism fails, we have a way to move the entire timelock contract back onto Ethereum. Luckily thatâs not so difficult, but itâs more complex than it would be on the sidechain.
Things get really complex when itâs not so clear how the thing on the plasma chain gets to move back onto Ethereum. A timelock contract thatâs just holding your money makes sense because it seems obvious that you should be able to move the contract. But what if weâre talking about a timelock contract thatâs holding money for 100 people at once? Now itâs not so clear anymore.
Put simply, the major con of plasma chains is that you canât really do the same complex operations that you could do on sidechains. Importantly, though, the reason you canât do these complex things is because youâre taking more precautions in order to ensure that your funds stay safe.
Understanding: The Plasma Chain Operator¶
When learning about plasma youâll eventually run into the idea of the plasma chain âoperatorâ. For the most part, the operator is exactly what it sounds like â a single entity thatâs responsible for aggregating transactions into blocks and then publishing those blocks to some other âmainâ blockchain (like Ethereum). Blockchains are only usable if new blocks are being created, so, by producing these new blocks, the operator is quite literally responsible for keeping the whole plasma chain running.
This might be a little confusing at first. Initial reactions are usually something along the lines of, âWhat? Plasma chains have operators? Arenât blockchains supposed to be decentralized?â Well, it turns out that the answer is, as you mightâve guessed, âKinda.â
Back to Basics¶
Letâs go back to basics and talk a little bit about why most blockchains use decentralized block production mechanisms in the first place. Blockchains are big logs of things that have happened, broken into concrete and ordered time-steps we call blocks. Sometimes these âthings that have happenedâ are simple - âA sent X amount of money to Bâ - sometimes theyâre more complex - âKitty A mated with Kitty B and created Kitty Câ. No matter what these events are, we usually want to make sure that we have a few key properties:
- No one should be able to re-write history.
- No one should be blocked from making transactions.
Letâs imagine we have a blockchain run by a single person. For the sake of argument, assume that you must use the blockchain run by that person for some reason. Well, unfortunately itâs quite easy for that person to break the first property. If that person says âtransaction X happenedâ and then later says âtransaction X never happenedâ, thereâs not much you can really do. You know that the transaction happened, but the blockchain itself doesnât.
Itâs also really easy for that person to break the second property. If they donât want you to send transactions, they can just refuse to add any transactions that come from you. Had a bunch of money on that blockchain? Too bad, youâre not getting it back.
Plasma Magic¶
The problems with blockchains run by a single person are why blockchains usually have fancy mechanisms that ensure that itâs extremely expensive to rewrite history. Itâs also why blockchains usually have lots of different people who can create blocks â no single person can stop someone from making transactions. So why can we have a single person running plasma chain? Itâs because we cheat (sort of).
In plasma world, we get the first property by taking advantage of the âmain blockchainâ that we were talking about earlier. Plasma chain operators need to publish a block âcommitmentâ (sort of like a very compressed version of the block) to the main blockchain for every block they produce. A smart contract on the main blockchain ensures that the operator can never publish the same block twice. As long as the main blockchain has the first property, so does the plasma chain! Thereâs no way for the operator to re-write history unless they can re-write history on the main chain.
The second property (blocking users from transacting) is where things get interesting. Unfortunately, itâs still possible for the operator to censor transactions from anyone they want. However, this is where the magic of plasma comes in. Plasma chains are designed in a way that no matter what, a user can always withdraw their money from the plasma chain back to the main chain. Even if the operator is actively trying to steal money from you, youâll still be able to get it back. Being censored obviously isnât great, but itâs not as bad when you can always take your money somewhere else.
Decentralizing the Operator¶
The one thing that wasnât really mentioned here is the fact that the âoperatorâ can actually consist of multiple people making decisions about what blocks to publish. This could be as simple as a having a few designed people who take turns making blocks, or as complex as a Proof-of-Stake system that selects block producers randomly. Either way, itâs complicated than just having a single person run everything, but itâs probably the way to go for projects that want to get rid of censorship. At the same time, it tends to be unimportant research-wise whether the operator is a single person or many people. As a result, youâll often see people just assuming the operator is a single person for simplicity.
Generalized Plasma State Spec¶
- Data Structures
stateID: uint
- the index of unique, non-fungible states in a plasma chainstateObject
struct:predicate: address
- the stateâs predicate rulesetparameters: bytes
- input parameters to the predicate ruleset
stateUpdate
structstate: stateObject
- the new state for this range ofstateID
sstart: uint
- the start of the range ofstateID
send: uint
- the end of the range ofstateID
splasmaBlockNumber: uint
- the plasma block in which a committment was madeplasmaContract: address
- the address of the plasma contract the state update is meant for
stateUpdateWitness
structinclusionProof: bytes[]
- array of sibling nodes forming the Merkle inclusion proof
deposit
struct:state: stateObject
- the initial state of the deposited coinstart: uint
- the firststateID
of the deposit. The plasma contract stores a mapping fromdepositEnd->deposit
precedingPlasmaBlockNumber: uint
- the most recent plasma block leading up to the deposit.
exitableRange
struct:start: uint
- The firststateID
of a still-exitable range. The plasma contract stores a mapping fromcexitableRangeEnd->exitableableRange
isSet: bool
- whether or not this value in the mapping has been initialized. Needed because EVM canât differentiate between a mapping set to 0 and an unset mapping.
exit
struct:update: stateUpdate
- the state being claimed.exitStart
- the start of the updateâs subrange claiming to be undeprecated.exitEnd
- the end of the updateâs subrange claiming to be undeprecated.ethBlockRedeemable: uint
- when theexit
âs dispute period expires.numChallenges: uint
- the number of pending challenges preventing aexit
âs redemption.
challenge
struct:earlierExitID: uint
- the earlier undeprecatedexit
being used to challenge an invlaidexit
.laterExitID: uint
- the challengedexit
.
- Commitment Contract
- Public Variables:
operator: public(address)
- the operatorâs address.nextPlasmaBlockNumber: public(uint256)
- what the block number of the next block committment will be.lastPublish: public(uint256)
- the Ethereum block number of the last plasma block.blockHashes: public(map(uint256, bytes32))
- the blocks submitted so far.
- Methods:
setup
- Used to initiate the contract with collator pubkeycommitBlock
: allows the operator to submit sequential blocksverifyUpdate(update: stateUpdate, subject: address, stateUpdateWitness: bytes)
: returns bool whether a givencommitment
was made on behalf of a givensubject
address (this is the plasma contract for e.g. a specific ERC20), at a the givenplasmaBlockNumber
, based on a validcommitmentWitness
- Block Structure/Proof Validity, as checked by
verifyUpdate
: - merkle node format:
[hash: bytes32][subject: address][index: bytes16]
- merkle parent function:
parent(leftSibling, rightSibling) = [sha256([leftSibling][rightSibling])][rightSibling.subject][rightSibling.index]
- merkle proof format:
siblingMerkleNodes[]
- array of the siblings going up the branch - branch validity requires that left
siblingMerkleNodes
going up the branch are monotonically decreasin - For a given
commitment
,subject
, andcommitmentWitness
, where theleaf
is the bottommost node in thestateUpdateWitness
we must have: leaf.subject == subject
stateUpdate.end <= leaf.index
stateUpdate.subject = leaf.subject
stateUpdate.start >= START
whereSTART
is either the merkle proofâs deepest left sibling, or0
if none exist (this is only the case for the ``0``th element in the tree)
- For a given
- leaf nodes are parsed to
[hash(state)][subject][state.end]
- NOTE: for any nodes in the tree whose sibling has the same
subject
address, we may remove the address for efficiency, as long as the above conditions are met as if thesubject
is prepended to the index. This is definitely an optimization to consider down the line!
- NOTE: for any nodes in the tree whose sibling has the same
- leaf nodes are parsed to
- merkle node format:
- Block Structure/Proof Validity, as checked by
- Plasma Contract
- Public Variables:
self.commitmentAddress
- where the operator is submitting commitmentsself.tokenAddress
- the ERC20 contract of for this plasma contract (weâll have one contract per token)self.deposits[end: uint] -> deposit
- mapping of all deposits todeposit
structsself.exitableRanges[end: uint] -> exitableRange
- mapping of all the unclaimed ranges (âstates still in the plasma chainâ)self.exits[exitID] -> exit
- all of the current exitsself.challenges[challengeID] -> challenge
- all of the current challenges on exitsself.DISPUTE_PERIOD: uint
- the minimum dispute period before a claim can be redeemed
- Public methods:
deposit(amount, state)
- Deposits specify an initial state and the amount of money being deposited into that state
- adds to
self.deposits
- extends
self.claimableRanges
so that the state is now claimable
exitStateUpdate(exitStart: uint, exitEnd: uint, update: stateUpdate, updateWitness: stateUpdateWitness, initiationWitness: bytes)
- allows users to submit a claim on a committed stateassert verifyUpdate(update, self.address, stateUpdateWitness)
assert exitStart >= update.start
assert exitEnd <= update.end
assert update.state.predicate.can_initiate_exit(update, initiationWitness)
- if so, adds a new exit to
self.exits
- sets the exitâs
ethBlockRedeemable
to:eth.block + self.CHALLENGE_PERIOD + state.predicateAddress.getAdditionalLockup(update)
exitDeposit(exitStart: uint, exitEnd: uint, depositEnd: uint, claimabilityWitness:bytes)
- allows users to submit an exit on a deposited state- both of the above store an
exit
struct inself.exits[self.exitNonce]
and incrementself.exitNonce
. - sets the claimâs
ethBlockRedeemable
to:eth.block + self.CHALLENGE_PERIOD + state.predicateAddress.getAdditionalLockup(state)
- In this case, the
update.plasmaBlockNumber
comes from thedeposit.precedingPlasmaBlockNumber
- both of the above store an
challengeExit(earlierExitID, laterExitID)
- allows users to challenge a later exit with an earlier undeprecated exit- this is the way we challenge exits if the operator commits some a state with something undprecated in the history. The function checks that:
earlierExitID
âs claimed range intersects that oflaterExitID
earlierExitID.update.plasmaBlockNumber < laterExitID.update.plasmaBlockNumber
eth.block < laterExit.ethBlockRedeemable
- if so, it does the following:
- create a
challenge
object inself.challenges[challengeNonce]
- increment
challengeNonce
- increase the
laterExit.ethBlockRedeemable
toearlierExit.ethBlockRedeemable
if the latter is bigger - increment
challengedClaim.numChallenges
- create a
cancelDeprecatedExit(stateID: uint, exitID: uint, deprecationWitness: bytes)
- allows users to cancel an exit by demonstrating adeprecationWitness
for one of the ``state``s in its rangeexit = self.exits[exitID]
assert exit.update.predicateAddress.verifyDeprecation(stateID, exit.update, deprecationWitness)
- if so, clears the exit, deleting it from the
self.exits
mapping
removeChallenge(challengeID: uint)
- allows users to remove a challenge- checks that the
self.challenges[challengeID].earlierExit
has been revoked, i.e. that its key is no longer set to a value in self.exits[] - if so, decrements the
self.exits[self.challenges[challengeID].laterExitID].numChallenges
and then clears/deletesself.challenges[challengeID]
- checks that the
finalizeExit(exitID, exitableRangeEnds)
- asserts
exit
âs numChallenges = 0 - tries
isRangeClaimable
for the variousclaimableRangeEnds
, reverts if none pass the check - asserts the current
eth.block >= exit.ethBlockRedeemable
- approves the ERC20 claim amount (
=start-end
) to be transferred by theexit.update.state.predicateAddress
- calls
finalizeExit(update)
on theupdate.state.predicateAddress
- asserts
- Predicate interface
- Public methods/interface:
verifyDeprecation(stateID: uint, update: stateUpdate, deprecationWitness: bytes) -> bool
- returns true/false whether a givendeprecationWitness
is valid (if true the exit may be cancelled)finalizeExit(update: stateUpdate)
- called once a claim on a state is redeemed on the plasma contract- in principle, this can do anything, but will almost always call the
ERC20.transferFrom
function to the tune ofexit.start - exit.end
, either to itself to initiate an additional dispute period, or to some ultimate beneficiary as devised from theexit.update.state.parameters
- in principle, this can do anything, but will almost always call the
canInitiateExit(update: stateUpdate, initiationWitness: bytes) -> bool
- returns true/false whether a claimant is eligible to submit an exit on a given stategetAdditionalDisputePeriod(update: stateUpdate)
- returns an additional number of ETH blocks which must elapse, in addition to the standardplasmaContract.DISPUTE_PERIOD
, before the exit may be redeemed
- Predicate Examples
- Simple Ownership
struct ownershipDeprecationWitness:
newStateUpdate: stateUpdate
newUpdateWitness: stateUpdateWitness
signature: signature
public function verifyDeprecation(stateID: uint, update: stateUpdate, revocationWitness: bytes):
assert verifyUpdate(deprecationWitness.newStateUpdate, revocationWitness.newUpdateWitness)
assert verifySignature(revocationWitness.newStateUpdate, signature) = update.state.owner
public function finalizeExit(exit: exit):
redeemedAmount: uint = exit.end - exit.start #length of sequential stateIDs claimed
ERC20.transferFrom(self.address, exit.update.state.owner, )
public function canInitiateExit(update: stateUpdate, initiationWitness: bytes)
:- assert tx.sender = commitment.state.parameters.owner``
Multisig
Atomic Swap
- Basic Payment Channel
- struct
stateChannelParameters
: participants: address[]
- array of pubkeys participating in the channelopeningUpdatesHash: bytes32
- a hash of all thestateUpdate
objects which must be made for the channel to be considered successfully âopenedâfailedOpeningRecipient: address
- the person to send money to if the opening failed, i.e. the above commitments werenât madeonChainChannel: address
- the on-chain payment channel to send the money to if channel isnât closed out on-chaincallData: bytes[]
- the instantiation data passed to theonChainChannel
- struct
- struct
stateChannelDeprecationWitness
closureUpdates: stateUpdate[]
- array of the states agreed to close onclosureUpdateWitnesses: stateUpdateWitness[]
- array of the proofs that the above updates were madeclosureApprovals: signature[]
- array of signatures by each of thestate.parameters.participants
onhash(closureUpdates)
agreeing to close
- struct
- public
self.successfulOpenings[upeningUpdatesHash] -> bool
- mapping of whether or not a givenopeningUpdatesHash
was successfully made - public
proveOpenings(openingUpdates: commitment[], openingWitnesses: stateUpdateWitness[])
- allows users to prove that a state channel was successfully opened by validating all opening inclusions
- asserts that
verifyUpdate
for eachopeningUpdate
state and its witness - if so, sets ``self.successfulOpenings[hash(openingUpdates) = true]
- public
- struct
openingExitStatus
- the struct used if an open channel is being exited because of an unsuccessful closure totalCoins
- the total number of coins entered into the payment channelredeemedCoins
- the total number of coins whose claims have been redeemed so far
- struct
- public
self.openingExitsInProgress[upeningUpdatesHash:bytes32] -> openingExitStatus
- mapping of âin progressâ exits on opened channels verifyDeprecation
- asserts that
self.openingClaimsInProgress[update.parameters.openingUpdatesHash)].redeemedCoins == 0
â if any of the opening state has been redeemed, all state must be redeemed from the openings, no revocation is valid. - asserts that
verifyUpdate
for each commitment in the revocation witness - asserts that each
state.parameters.participants
signed off onhash(closureUpdates)
- asserts that
finalizeExit
- checks whether the channel was successfully opened:
assert self.successfulOpenings[openingUpdatesHash]
- If it was:
self.openingExitsInProgress[openingUpdatesHash].redeemedCoins += exit.end - exit.start
- let
exitInProgress = self.openingExitsInProgress[openingCommitmentsHash]
- if
exitInProgress.redeemedCoins == exitInProgress.totalCoins
, then forward thetotalCoins
to theexit.update.parameters.onChainChannel(exit.update.parameters.callData)
â the opening has been fully claimed and the on-chain channel may take over.
- Otherwise, not all money in the channel has been redeemed from the plasma contract yet, so we must wait.
- checks whether the channel was successfully opened:
- L1<>L2 liquidity predicate (swap PETH for ETH)
- struct
tradeParameters
: tradeID: uint
- a unique ID for the tradeseller: address
saleAmount: uint
- the amount of ETH the coins are being sold for
- struct
- struct
trade
ethSender: address
targetPlasmaBlock: uint
- struct
- mapping
self.trades[tradeID][ethRecipient][amount] -> trade
maps the unique aspects of the trade to the sender and intended block of the new ownership state committment - public method:
submitTrade(tradeID: bytes32, ethRecipient: address, targetPlasmaBlock: uint)
- assert that the next plasma block is the
targetPlasmaBlock
- assert that
self.trades[tradeID: bytes32][ethRecipient: address][tx.value: uint]
is unset - if not:
- set the value with
trade.ethSender = tx.Sender
andtrade.targetPlasmaBlock = targetPlasmaBlock
- forward the ETH to
ethRecipient
- set the value with
- assert that the next plasma block is the
- public method:
verifyDeprecation
deprecationWitness
consists of:- a valid
newStateUpdate
, satisfying: .start
and.end
equalling the deprecatedstateUpdate
.start
and.end
- the existance of an entry in
self.trades[stateUpdate.parameters.tradeID][newStateUpdate.parameters.owner][end - start]
- the
ethSender
in that entry being thenewStateUpdate.parameters.owner
- the
newStateUpdate.plasmaBlockNumber == trade.targetPlasmaBlock
- the
- the existance of an entry in
- a valid
finalizeExit
- checks for the existence of an entry in
self.Trades[exit.state.parameters.tradeID][redeemedState.seller][end - start]
- if it exists, send to that
trade.ethSender
- otherwise, send back to
redeemedState.parameters.seller
- if it exists, send to that
- checks for the existence of an entry in
Contributing to @pigi¶
Welcome! A huge thank you for your interest in contributing to Plasma Group. Plasma Group is an open source initiative developing a simple and well designed plasma implementation. If youâre looking to contribute to a Plasma Group project, youâre in the right place! Itâs contributors like you that make open source projects work, we really couldnât do it without you.
We donât just need people who can contribute code. We need people who can run this code for themselves and break it. We need people who can report bugs, request new features, and leave helpful comments. We need you!
Weâre always available to answer your questions and to help you become a contributor! You can reach out to any of the members of Plasma Group on GitHub, or send us an email at contributing@plasma.group.
Here at Plasma Group weâre trying to foster an inclusive, welcoming, and accessible open source ecosystem. The best open source projects are those that make contributing an easy and rewarding experience. Weâre trying to follow those best practices by maintaining a series of resources for contributors to Plasma Group repositories.
If youâre a new contributor to @pigi/core
, please read through the following information.
These resources will help you get started and will help you better understand what weâre building.
Contributing Guide and Code of Conduct¶
Plasma Group follows a Contributing Guide and Code of Conduct adapted slightly from the Contributor Covenant. All contributors are expected to read through this guide. Weâre here to cultivate a welcoming and inclusive contributing environment. Every new contributor needs to do their part to uphold our community standards.
Getting Started as a Contributor¶
Requirements and Setup¶
Cloning the Repo¶
Before you start working on a Plasma Group project, youâll need to clone our GitHub repository:
git clone git@github.com:plasma-group/pigi.git
Now, enter the repository.
cd pigi
Node.js¶
Most of the Plasma Group projects are Node.js applications.
Youâll need to install Node.js
for your system before continuing.
Weâve provided a detailed explanation of now to install Node.js on Windows, Mac, and Linux.
Yarn¶
Weâre using a package manager called Yarn. Youâll need to install Yarn before continuing.
Installing Dependencies¶
@pigi
projects make use of several external packages.
Install all required packages with:
yarn install
Building¶
@pigi
provides convenient tooling for building a package or set of packages.
Build all packages:
yarn run build
Build a specific package or set of packages:
PKGS=your,packages,here yarn run build
Linting¶
Clean code is the best code, so weâve provided tools to automatically lint your projects.
Lint all packages:
yarn run lint
Lint a specific package or set of packages:
PKGS=your,packages,here yarn run lint
Weâve also provided tools to make it possible to automatically fix any linting issues. Itâs much easier than trying to fix issues manually.
Fix all packages:
yarn run fix
Fix a specific package or set of packages:
PKGS=your,packages,here yarn run fix
Running Tests¶
@pigi
projects usually makes use of a combination of Mocha (a testing framework) and Chai (an assertion library) for testing.
Run all tests:
yarn test
Run tests for a specific package or set of packages:
PKGS=your,packages,here yarn test
Contributors: remember to run tests before submitting a pull request! Code with passing tests makes life easier for everyone and means your contribution can get pulled into this project faster.
Miscellaneous Reference¶
This page provides a series of miscellaneous reference articles that can be helpful when installing Plasma Group components.
Running a Terminal¶
Before you keep going, itâs probably good to become familiar with using the terminal on your computer. Here are some resources for getting started:
Installing Git¶
git
is an open source version control system.
You donât really need to know how it works, but you will need it in order to install most Plasma Group components.
Windows¶
Atlassian has a good tutorial on installing git
on Windows.
Itâs basically just installing an .exe
and running a setup wizard.
MacOS¶
Installing git
on a Mac is pretty easy.
You basically just need to type git
into your terminal.
If you have git
installed, youâll see a bunch of output.
Otherwise, youâll get a pop-up asking you to install some command-line tools (including git
).
Linux¶
Installing git
on Linux is also pretty easy.
However, the exact install process depends on your distribution.
Hereâs a guide for installing git
on some popular distributions.
Installing Node.js¶
Most of the Plasma Group apps are built in JavaScript and make use of a tool called Node.js.
In order to run our tools, youâll need to make sure that youâve got Node.js
installed.
Hereâs a list of ways to install Node.js
on different operating systems:
Windows¶
If youâre on a windows computer, you can download the latest Long-term Support (LTS) version of Node.js
here.
Youâll just need to install the .msi
file that Node.js
provides and restart your computer.
MacOS¶
You have some options if you want to install Node.js
on a Mac.
The simplest way is to download the .pkg
file from the Node.js
downloads page.
Once youâve installed the .pkg
file, run this command on your terminal to make sure everything is working properly:
node -v
If everything is working, you should see a version number pop up that looks something like this:
v10.15.1
Linux¶
There are different ways to install Node.js
depending on your Linux distribution.
Hereâs an article that goes through installing Node.js
on different distributions.
Our Lovely Contributors¶
This is just a quick high-level overview of the different types of contributors we can expect to work on Plasma Group projects! All of our contributors should be treated with respect, and theyâre all equally important. Itâs important to understand the different types of people who might be helping out with Plasma Group projects so we can streamline the contributing experience (and make sure everyone feels the love they deserve!).
New Contributors¶
New Contributors are people who want to get involved with PG but might not know how yet. This is the âbucketâ category for the remaining personas and New Contributors are eventually funnelled into one of the other categories. These users can be pretty much anyone - software engineers, crypto enthusiasts, family members, whoever.
Generally, we want to help new contributors become active contributors. For example, we might want to direct someone to resources that help them become a Beta Testing Contributor. This means that we should develop a set of strong resources where a new contributor can land and figure out where they want to help out.
Co-Developing Contributors¶
Co-Developing Contributors are people working on the PG code in any one of our repositories. These people are doing all sorts of work, like tackling issues, adding new features, or writing new documentation. This work is usually happening in the Plasma Group repositories.
These contributors want things to work on, and want a good experience while doing so. You wouldnât be working on a project if you couldnât figure out what to do or if you got a terrible response every time you tried to contribute!
Co-Developers are extremely important and we need to make sure that theyâre always treated with respect. We should be responding as quickly as possible, reviewing their PRs, and rewarding them for their hard work.
Beta Testing Contributors¶
Beta Testing Contributors are users helping us test our code, but not necessarily writing code. Often these contributors donât have the time to contribute lots of code because theyâre busy with other projects. However, these users still want to give back to the community by seeing how things work and playing with new features. These users want to make sure that their feedback is heard and that their time spent beta testing isnât for nothing. Adding features or fixing bugs quickly will make beta testers feel like thereâs constantly something new to explore.
Beta Testing Contributors should be treated as a great resource, because they are. Without them, we wouldnât find all of the various bugs weâre inevitably going to have. Thereâs no such thing as production-quality software without beta testing :-).
Passive Contributors¶
Passive Contributors are users who are interacting with PG but arenât actively working on issues in one of our GitHub repositories.
These users are typically people building applications on top of our projects but not working on the underlying code.
For example, this might be someone building a wallet using @pigi/plasma-js
.
Most of the time, these users donât come in intending to make significant changes to the code that theyâre making use of - they usually just expect it to work. When they do interact with the codebase, it tends to be to report a bug or ask for a new feature. These users are important because they find bugs in production and request features that help us build things out for real use-cases.
External Contributors¶
There are lots of people who support us but arenât specifically helping out by making issues, tackling issues, or requesting features. This could be people on twitter discussing our work, sitting near us when weâre working, or just providing moral support. We have to care for these people because theyâre what keep us motivated and pushing to build the best projects we can! Let them know theyâre appreciated because we wouldnât be here without them.
Community Interactions¶
Community members are the single most important resource available to Plasma Group. With such a small set of core contributors, we can only hope to create the best possible plasma implementation by tapping into our community. Remember, we cannot succeed without the support of our community.
In the words of Eric Raymond:
If you treat your beta-testers as if theyâre your most valuable resource, they will respond by becoming your most valuable resource.
Itâs extremely important that we keep this fact in mind during each and every interaction with a community member, on- or offline.
General Guidelines¶
- Treat all contributors with respect.
Our contributors are taking time out of their busy days to help us make plasma a reality. That alone deserves a lot of respect! Our contributors are also real people with real lives. Plasma Group core contributors should treat all outside contributors with the same respect they show each other.
- Always thank contributors.
Whether itâs help with a bug report, feature request, or pull request, our contributors make a huge dent in the amount of work placed on the core team. Thank contributors for their help! Itâs nice and itâs the right thing to do, but itâs also important to make contributors feel appreciated. Most of the people helping us arenât doing it for the money, and they definitely donât want to receive a harsh response for trying to help.
Even if a user has submitted something like a duplicate issue, thank them for their assistance! Theyâll feel like they can help out without judgement.
- Show contributors that their contributions are making a difference.
Contributors want to make a difference, and itâs important that their impact is acknowledged. Again, always thank contributors for their help. Feel free to reach out to contributors privately and thank them for their contributions, especially after big releases.
Another great way to thank contributors for their work is to send GitCoin Kudos. Kudos are special ERC-721s that you can send to users via their GitHub usernames!
- Help contributors get involved.
This might be the most important rule of them all. Always be focused on helping contributors get involved. Whether thatâs by Tweeting out GitHub issues, linking users to documentation, or writing better issues, your number one focus should be to build out the community of contributors.
GitHub Interactions¶
Most of our interactions with community members happen through GitHub. As a result, itâs important that the key GitHub interactions are well planned out. This section describes the most important interactions (Bug Reports, Feature Requests, and Pull Requests) and provides basic guidelines for how to best work with community members.
Remember that everyone who contributes to our projects is taking time out of their day. Always thank people for helping out, even if youâre closing out an issue for being a duplicate of something else! The golden rule is the best rule :-).
Bug Reports¶
- If a potential active contributor submits an issue, give them the resources to become an active contributor.
A lot of the time, users submitting an issue arenât familiar with the underlying code. Theyâre probably submitting an issue because they canât find an easy fix! If you just tell the user that youâre âon itâ or something similar, youâre likely making the situation worse.
First, thank the contributor for reporting the issue in a timely manner. Theyâre taking time out of their day to report a problem and, if theyâre like a lot of us, itâs probably something thatâs caused them a headache. A quick acknowledgment will let them know that youâre available, responsive, and are taking their issue seriously.
If the user hasnât provided enough information, make sure to politely ask for information that you think might be relevant. Always make sure the user provides steps to reproduce the problem so that it can be more easily solved. Unless you know otherwise, never assume that the user is particularly technical. Help them out by giving them the exact commands and relevant output thatâll help solve the issue.
Todo
Create a basic diagnostics guide that helps users figure out whatâs going on with their projects.
- Get to the source of the issue.
Next is getting to the source of the issue. This is where things get fun! The first thing youâll want to do is to assess the general difficulty of the underlying problem. Using the steps to reproduce, try to locate the problematic areas of the software. Is it an encoding issue? Is the wrong thing being passed to another function? Is something undefined when it shouldnât be? Is it a problem thatâll require fixes in ten different places?
Depending on the difficulty of the problem, youâll want to take different next steps. If the problem is relatively simple, great! This is an awesome opportunity to convert the potential contributor into an active one. If a problem is simple, unless the itâs absolutely critical and needs an immediate fix, your top priority should be to give someone else the tools to solve the problem. Remember, letting potential contributors get their hands into some code is the best way to convert them to active contributors.
- Try to give people the tools to solve problems themselves.
Giving someone the tools to solve the issue for themselves is a multi-step process. First, youâll want to provide the user with all the necessary background information. Explain the different relevant components and then explain what you think is probably the general cause of the issue. Feel free to tag another contributor if you think theyâd have a better understanding of the problem. Your next steps depend on whether you know exactly whatâs causing the issue.
If you donât know exactly whatâs causing the issue, this is a good opportunity to ask a potential contributor if theyâd like to step in and solve the issue! This might be the person who submitted the issue or it might be another contributor that you tag. If itâs not the person who submitted the issue, try to think about which contributor would most benefit from working on the issue.
If you already know whatâs causing the issue, try to explain the cause in a very detailed manner. You can even go as far as pointing out specific lines that are causing problems or sketching out a potential solution. You donât necessarily want to entirely solve the problem for someone else, but you do want to give enough leads if possible.
Feature Requests¶
- Clarify the exact parameters of the feature request.
Sometimes contributors are already very familiar with our codebase/functionality and have a clear idea of what new features theyâd like. Other times contributors have a general idea of what theyâd like, but we still need to figure out exactly how the feature will work. Itâs important to start off by clarifying exactly how the new feature will work. Clear feature requests are the best way to make sure that weâre implementing exactly what was requested. We definitely donât want to spend a lot of time building something out that isnât useful!
- Figure out what changes would need to be made for the new feature.
Understanding the scope and impact of any changes is necessary for figuring out a timeline. Once youâve clarified exactly what feature is being requested, you can start figuring out what needs to be changed in order to add the feature. If youâre familiar with the codebase, this is a great time to point to the files that would need to be changed. Otherwise, feel free to tag any other contributors who might have a better idea about the problem!
- Figure out a timeline for the new feature.
We want to make sure that contributors who request new features get a timeline so they know how long theyâll need to wait. Contributors might otherwise turn to another project with a more explicit roadmap. Try to guesstimate the amount of time that a feature will take to complete. Also think about where the feature fits in with other features because it might make more sense to release several updates simultaneously. Definitely discuss with other contributors if youâre not sure on an exact timeline.
- Keep people updated with the status of a new feature.
Figuring out an exact timeline is more of an art than a science. There are always unexpected things that might speed up (or slow down!) the addition of a new feature. Itâs important that we keep a feature request thread updated with the latest work on a feature.
Duplicate Requests¶
- Make sure the request is actually a duplicate.
Sometimes we get duplicate bug reports or feature requests. This means that someone has created an issue thatâs already been created before. Duplicates are easy to handle well but theyâre also easy to handle poorly.
The very first thing you should do is make sure that the issue is actually a duplicate! Sometimes people submit very similar issues that have subtle differences. These subtle differences can actually have a huge impact on the required fixes.
- Be nice about it!
Contributors who submit duplicates still did the same amount of work as the contributor who submitted the original issue. Itâs disappointing realizing that you did duplicate work, so make sure that they understand we still appreciate their work immensely. Maintainers too often treat duplicates like throwaway issues and close them without much interaction. Thank contributors for their work and direct them to the new thread so they can continue to help out.
- Close the duplicate issue and label it.
This is the last step! Make sure to close the duplicate (but not before being nice) and label it as a duplicate for the future.