···2626 - ...but export only works with timestamps (sequence-based export and websocket-based export is not implemented)
2727- Validation of operations (hopefully it's foolproof enough - needs revising, strengthening... and nullification might not be as simple as I made it out to be?)
2828- Snapshot-based sync for quickly bringing replicas up to date
2929+- An initial, not very well tested, version of the "authoritative import" that gradually brings in operations from the official plc.directory
29303031## What's yet to be implemented
31323232-- Actually syncing with the authoritative source, plc.directory (I am in the middle of this, and I am not liking how many doubts I have about how to approach it)
3333+- Actually syncing with the authoritative source, plc.directory (in progress - fetching from the official directory is mostly implemented as indicated above; submitting blockchain happenings to the official directory is yet to be implemented)
3334- Spam/abuse prevention (typical blockchains do this with transaction fees and some lesser known ones - e.g. Nano - use proof of work, but this is not a cryptocurrency and PoW is a bit ewww)
3435- A process for nodes to become validators. Unless everyone agreed that it's best if the set of validators is centralized. I mean, it's not worse than the current state of plc.directory while still allowing people to easily have their own local synced mirror through the magic of CometBFT...
3536- Testing, testing, testing, validation, validation, validation...
···72737374You need to install Go, in case the go.mod and multiple *.go files didn't make it obvious.
74757575-To run a single node, you want to use `startfresh.sh`. To run multiple nodes, there's `startfresh-testnet.sh` but note that you'll have to go into the config.toml of every node **except one**, and add
7676-7777-```
7878-[plc]
7979-laddr = ""
8080-```
8181-8282-to prevent the PLC API server from coming up on those nodes (since by default it'd try listening on the same port that's already used by one of the other replicas, and fail to launch).
7676+To run a single node, you want to use `startfresh.sh`. To run multiple nodes, there's `startfresh-testnet.sh`. Note that using the created config files, only the first node will serve the PLC API.
83778478The PLC API server listens on `127.0.0.1:28080` by default and other fun facts you could learn from reading config.go. There are other ports that the server listens on, one that exposes the CometBFT RPC API, and also the P2P ports for communication between the nodes.
8579
+32-27
abciapp/execution.go
···77 "time"
8899 abcitypes "github.com/cometbft/cometbft/abci/types"
1010+ cbornode "github.com/ipfs/go-ipld-cbor"
1011 "github.com/palantir/stacktrace"
1112 "github.com/samber/lo"
1212- "tangled.org/gbl08ma/didplcbft/store"
1313)
14141515// InitChain implements [types.Application].
···2222func (d *DIDPLCApplication) PrepareProposal(ctx context.Context, req *abcitypes.RequestPrepareProposal) (*abcitypes.ResponsePrepareProposal, error) {
2323 defer d.tree.Rollback()
24242525+ if req.Height == 2 {
2626+ tx := Transaction[SetAuthoritativePlcArguments]{
2727+ Action: TransactionActionSetAuthoritativePlc,
2828+ Arguments: SetAuthoritativePlcArguments{
2929+ PLCURL: "https://plc.directory",
3030+ RestartImport: true,
3131+ },
3232+ }
3333+3434+ out, err := cbornode.DumpObject(tx)
3535+ if err != nil {
3636+ return nil, stacktrace.Propagate(err, "")
3737+ }
3838+3939+ req.Txs = append(req.Txs, out)
4040+ }
4141+2542 st := time.Now()
2643 acceptedTx := make([][]byte, 0, len(req.Txs))
2744 toProcess := req.Txs
···3350 return nil, stacktrace.Propagate(err, "")
3451 }
35523636- if result.isAuthoritativeImportTransaction {
3737- // this type of transaction is not meant to appear in the mempool,
3838- // but maybe it's not impossible that a non-compliant node could have gossiped it to us?
3939- // (not sure if CometBFT checks transactions coming from other peers against CheckTx)
4040- continue
4141- }
4242-4353 if result.Code == 0 {
4454 acceptedTx = append(acceptedTx, tx)
4555 } else {
···69797080 if err == nil && len(maybeTx) != 0 {
7181 totalSize := lo.SumBy(acceptedTx, func(tx []byte) int { return len(tx) })
7272- // 4K safety margin
8282+ // 4 KB safety margin
7383 if totalSize+len(maybeTx) < int(req.MaxTxBytes)-4096 {
7484 // we have space to fit the import transaction
7585···94104 return &abcitypes.ResponseProcessProposal{Status: abcitypes.ResponseProcessProposal_REJECT}, nil
95105 }
961069797- if req.Height == 1 {
9898- tree, err := d.MutableTree()
9999- if err != nil {
100100- return nil, stacktrace.Propagate(err, "")
101101- }
102102-103103- err = store.Tree.SetAuthoritativePLC(tree, "https://plc.directory")
104104- if err != nil {
105105- return nil, stacktrace.Propagate(err, "")
106106- }
107107- }
108108-109107 // if we return early, ensure we don't use incomplete results where we haven't voted ACCEPT
110108 d.lastProcessedProposalHash = nil
111109 d.lastProcessedProposalExecTxResults = nil
···119117120118 txResults := make([]*processResult, len(req.Txs))
121119 for i, tx := range req.Txs {
122122- result, err := processTx(ctx, d.transactionProcessorDependencies(), tx, req.Time, true)
120120+ result, action, processor, err := beginProcessTx(tx)
123121 if err != nil {
124122 return nil, stacktrace.Propagate(err, "")
125123 }
124124+ if result.Code == 0 {
125125+ if action == TransactionActionAuthoritativeImport && i != len(req.Txs)-1 {
126126+ // if an Authoritative Import transaction is present on the block, it must be the last one
127127+ return &abcitypes.ResponseProcessProposal{Status: abcitypes.ResponseProcessProposal_REJECT}, nil
128128+ }
129129+130130+ result, err = finishProcessTx(ctx, d.transactionProcessorDependencies(), processor, tx, req.Time, true)
131131+ if err != nil {
132132+ return nil, stacktrace.Propagate(err, "")
133133+ }
134134+ }
135135+126136 // when preparing a proposal, invalid transactions should have been discarded
127137 // so, if something doesn't succeed now, something has gone wrong and we should not vote in agreement of the proposal
128138 if result.Code != 0 {
129129- return &abcitypes.ResponseProcessProposal{Status: abcitypes.ResponseProcessProposal_REJECT}, nil
130130- }
131131-132132- if result.isAuthoritativeImportTransaction && i != len(req.Txs)-1 {
133133- // if an Authoritative Import transaction is present on the block, it must be the last one
134139 return &abcitypes.ResponseProcessProposal{Status: abcitypes.ResponseProcessProposal_REJECT}, nil
135140 }
136141
+15-7
abciapp/mempool.go
···10101111// CheckTx implements [types.Application].
1212func (d *DIDPLCApplication) CheckTx(ctx context.Context, req *abcitypes.RequestCheckTx) (*abcitypes.ResponseCheckTx, error) {
1313- result, err := processTx(ctx, d.transactionProcessorDependencies(), req.Tx, time.Now(), false)
1313+ result, action, processor, err := beginProcessTx(req.Tx)
1414 if err != nil {
1515 return nil, stacktrace.Propagate(err, "")
1616 }
1717- if result.isAuthoritativeImportTransaction {
1818- // this type of transaction is meant to be included only by validator nodes
1919- return &abcitypes.ResponseCheckTx{
2020- Code: 4002,
2121- Info: "AuthoritativeImport transactions can only be introduced by validator nodes",
2222- }, nil
1717+ if result.Code == 0 {
1818+ if action == TransactionActionAuthoritativeImport {
1919+ // this type of transaction is meant to be included only by validator nodes
2020+ return &abcitypes.ResponseCheckTx{
2121+ Code: 4002,
2222+ Info: "AuthoritativeImport transactions can only be introduced by validator nodes",
2323+ }, nil
2424+ }
2525+2626+ result, err = finishProcessTx(ctx, d.transactionProcessorDependencies(), processor, req.Tx, time.Now(), false)
2727+ if err != nil {
2828+ return nil, stacktrace.Propagate(err, "")
2929+ }
2330 }
3131+2432 return &abcitypes.ResponseCheckTx{
2533 Code: result.Code,
2634 Data: result.Data,
···47474848 # Adjust P2P listen address
4949 sed -i "s|^laddr = \"tcp://0.0.0.0:26656\"\$|laddr = \"tcp://$p2p_ip:26656\"|g" "testnet/node$i/config/config.toml"
5050+5151+ if [ "$i" -ne 0 ]; then
5252+ echo -e "\n[plc]\nladdr = \"\"" >> "testnet/node$i/config/config.toml"
5353+ fi
5054done
51555256# Configure rpc_servers for the last node (the one that will be started manually)