Skip to content

Commit

Permalink
[hardfork] Add a new pos hard fork detroit (#252)
Browse files Browse the repository at this point in the history
# Description

The PR merges detroit hard fork back to `dev` branch. The hard fork is
well tested in `DevNet`, and time to bump another release for the whole
`MainNet` network.

# Changes include

- [x] New feature (non-breaking change that adds functionality)
- [x] Breaking change (change that is not backwards-compatible and/or
changes current functionality)

# Breaking changes

Nodes should upgrade to the current version, and update its
`genesis.json` to accept the new `detroit` hard fork. Otherwise, its
block syncing would stop.

## Testing

- [x] I have tested this code with the official test suite

# Documentation update

The documentation would be updated when new version bumped.

Co-authored-by: 0xcb9ff9 <[email protected]>
Co-authored-by: 0xcb9ff9 <[email protected]>
Co-authored-by: abrahamcruise321 <[email protected]>
  • Loading branch information
4 people authored Nov 14, 2022
1 parent a977703 commit 4205200
Show file tree
Hide file tree
Showing 92 changed files with 3,743 additions and 1,226 deletions.
3 changes: 1 addition & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@ jobs:
go-version: 1.17.x

- name: Build Dogechain
# run: go build -tags netgo -ldflags="-s -w -linkmode external -extldflags "-static" -X \"github.com/dogechain-lab/dogechain/versioning.Version=${GITHUB_REF_NAME}\" -X \"github.com/dogechain-lab/dogechain/versioning.Commit=${GITHUB_SHA}\"" && tar -czvf dogechain.tar.gz dogechain
run: go build -a -o dogechain . && tar -czvf dogechain.tar.gz dogechain
run: go build -ldflags="-X \"github.com/dogechain-lab/dogechain/versioning.Version=${GITHUB_REF_NAME}\" -X \"github.com/dogechain-lab/dogechain/versioning.Commit=${GITHUB_SHA}\"" -a -o dogechain . && tar -czvf dogechain.tar.gz dogechain
env:
CGO_ENABLED: 0
CC: gcc
Expand Down
77 changes: 54 additions & 23 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -1,34 +1,65 @@
run:
timeout: 3m
tests: true
# default is true. Enables skipping of directories:
# vendor$, third_party$, testdata$, examples$, Godeps$, builtin$
skip-dirs-use-default: true

service:
golangci-lint-version: 1.49.0

linters:
# default false
disable-all: true
# Enable specific linter
# https://golangci-lint.run/usage/linters/#enabled-by-default-linters
enable:
- dogsled
- dupl
- errname
- errorlint
- forcetypeassert
- goconst
- gofmt
- gosec
- importas
- lll
- makezero
- misspell
- nlreturn
- nolintlint
- prealloc
- predeclared
- stylecheck
- thelper
- tparallel
- unconvert
- wastedassign
- whitespace
- wsl
- dogsled # Checks assignments with too many blank identifiers (e.g. x, , , _, := f())
- dupl # Code clone detection
- errname # Checks that sentinel errors are prefixed with the Err and error types are suffixed with the Error
- errorlint # errorlint is a linter for that can be used to find code that will cause problems with the error wrapping scheme introduced in Go 1.13
- forcetypeassert # Finds forced type assertions
- goconst # Repeated strings that could be replaced by a constant
- gofmt # Whether the code was gofmt-ed
- goimports # Unused imports
- gosec # Security problems
- importas # Enforces consistent import aliases
- lll # Long lines
- makezero # Finds slice declarations with non-zero initial length
- misspell # Misspelled English words in comments
- nlreturn # Checks for a new line before return and branch statements to increase code clarity
- nolintlint # Ill-formed or insufficient nolint directives
- prealloc # Finds slice declarations that could
- predeclared # Finds code that shadows one of Go's predeclared identifiers
- stylecheck # Stylecheck is a replacement for golint
- thelper # Detects golang test helpers without t.Helper() call and checks the consistency of test helpers
- tparallel # Detects inappropriate usage of t.Parallel() method in your Go test codes
- unconvert # Unnecessary type conversions
- wastedassign # Finds wasted assignment statements
- whitespace # Tool for detection of leading and trailing whitespace
- wsl # Forces you to use empty lines

linters-settings:
gofmt:
simplify: true
goconst:
min-len: 3
min-occurrences: 3

issues:
# new-from-rev: origin/dev # report only new issues with reference to dev branch
exclude-rules:
- path: _test\.go
linters:
- gosec
- unparam
- lll
- linters:
- staticcheck
path: "state/runtime/precompiled/base.go"
text: "SA1019:"
include:
- EXC0012 # Exported (.+) should have comment( \(or a comment on this block\))? or be unexported
- EXC0013 # Package comment should be of the form "(.+)...
- EXC0014 # Comment on exported (.+) should be of the form "(.+)..."
- EXC0015 # Should have a package comment
12 changes: 8 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,17 @@ protoc:
.PHONY: build
build:
$(eval LATEST_VERSION = $(shell git describe --tags --abbrev=0))
$(eval COMMIT_HASH = $(shell git rev-parse --short HEAD))
$(eval DATE = $(shell date +'%Y-%m-%d_%T'))
go build -o dogechain -ldflags="-X 'github.com/dogechain-lab/dogechain/versioning.Version=$(LATEST_VERSION)+$(COMMIT_HASH)+$(DATE)'" main.go
$(eval COMMIT_HASH = $(shell git rev-parse HEAD))
$(eval DATE = $(shell date -u +'%Y-%m-%dT%TZ'))
go build -o dogechain -ldflags="\
-X 'github.com/dogechain-lab/dogechain/versioning.Version=$(LATEST_VERSION)'\
-X 'github.com/dogechain-lab/dogechain/versioning.Commit=$(COMMIT_HASH)'\
-X 'github.com/dogechain-lab/dogechain/versioning.BuildTime=$(DATE)'" \
main.go

.PHONY: lint
lint:
golangci-lint run -c .golangci.yml --timeout=3m
golangci-lint run -c .golangci.yml

.PHONY: test
test: build
Expand Down
56 changes: 46 additions & 10 deletions blockchain/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/dogechain-lab/dogechain/blockchain/storage"
"github.com/dogechain-lab/dogechain/chain"
"github.com/dogechain-lab/dogechain/contracts/upgrader"
"github.com/dogechain-lab/dogechain/contracts/validatorset"
"github.com/dogechain-lab/dogechain/helper/common"
"github.com/dogechain-lab/dogechain/state"
"github.com/dogechain-lab/dogechain/types"
Expand Down Expand Up @@ -90,10 +91,13 @@ type Verifier interface {
ProcessHeaders(headers []*types.Header) error
GetBlockCreator(header *types.Header) (types.Address, error)
PreStateCommit(header *types.Header, txn *state.Transition) error
IsSystemTransaction(height uint64, coinbase types.Address, tx *types.Transaction) bool
}

type Executor interface {
ProcessBlock(parentRoot types.Hash, block *types.Block, blockCreator types.Address) (*state.Transition, error)
BeginTxn(parentRoot types.Hash, header *types.Header, coinbase types.Address) (*state.Transition, error)
//nolint:lll
ProcessTransactions(transition *state.Transition, gasLimit uint64, transactions []*types.Transaction) (*state.Transition, error)
Stop()
}

Expand Down Expand Up @@ -841,21 +845,20 @@ func (b *Blockchain) executeBlockTransactions(block *types.Block) (*BlockResult,
return nil, ErrParentNotFound
}

height := header.Number

blockCreator, err := b.consensus.GetBlockCreator(header)
if err != nil {
return nil, err
}

txn, err := b.executor.ProcessBlock(parent.StateRoot, block, blockCreator)
// prepare execution
txn, err := b.executor.BeginTxn(parent.StateRoot, block.Header, blockCreator)
if err != nil {
return nil, err
}

if err := b.consensus.PreStateCommit(header, txn); err != nil {
return nil, err
}

// upgrade system if needed
// upgrade system contract first if needed
upgrader.UpgradeSystem(
b.Config().ChainID,
b.Config().Forks,
Expand All @@ -864,6 +867,31 @@ func (b *Blockchain) executeBlockTransactions(block *types.Block) (*BlockResult,
b.logger,
)

// there might be 2 system transactions, slash or deposit
systemTxs := make([]*types.Transaction, 0, 2)
// normal transactions which is not consensus associated
normalTxs := make([]*types.Transaction, 0, len(block.Transactions))

// the include sequence should be same as execution, otherwise it failed on state root comparison
for _, tx := range block.Transactions {
if b.consensus.IsSystemTransaction(height, blockCreator, tx) {
systemTxs = append(systemTxs, tx)

continue
}

normalTxs = append(normalTxs, tx)
}

// execute normal transaction first
if _, err := b.executor.ProcessTransactions(txn, header.GasLimit, normalTxs); err != nil {
return nil, err
}

if _, err := b.executor.ProcessTransactions(txn, header.GasLimit, systemTxs); err != nil {
return nil, err
}

if b.isStopped() {
// execute stop, should not commit
return nil, ErrClosed
Expand Down Expand Up @@ -1015,8 +1043,9 @@ func (b *Blockchain) writeBody(block *types.Block) error {
}

// Write txn lookups (txHash -> block)
for _, txn := range block.Transactions {
if err := b.db.WriteTxLookup(txn.Hash, block.Hash()); err != nil {
for _, tx := range block.Transactions {
// write hash lookup
if err := b.db.WriteTxLookup(tx.Hash(), block.Hash()); err != nil {
return err
}
}
Expand Down Expand Up @@ -1052,10 +1081,17 @@ func (b *Blockchain) verifyGasLimit(header, parentHeader *types.Header) error {
diff *= -1
}

// gas limit should not count system transactions
limit := parentHeader.GasLimit / BlockGasTargetDivisor
// system transactions after detroit fork
if b.Config().Forks.IsDetroit(header.Number) {
// might be 2 txs.
limit += 2 * validatorset.SystemTransactionGasLimit
}

if uint64(diff) > limit {
return fmt.Errorf(
"invalid gas limit, limit = %d, want %d +- %d",
"limit = %d, want %d +- %d",
header.GasLimit,
parentHeader.GasLimit,
limit-1,
Expand Down
8 changes: 4 additions & 4 deletions blockchain/blockchain_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -971,10 +971,10 @@ func TestBlockchain_VerifyBlockBody(t *testing.T) {

executorCallback := func(executor *mockExecutor) {
// This is executor processing
executor.HookProcessBlock(func(
hash types.Hash,
block *types.Block,
address types.Address,
executor.HookProcessTransction(func(
transition *state.Transition,
gasLimit uint64,
txs []*types.Transaction,
) (*state.Transition, error) {
return nil, errUnableToExecute
})
Expand Down
10 changes: 5 additions & 5 deletions blockchain/storage/testing.go
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,7 @@ func testBody(t *testing.T, m PlaceholderStorage) {
Input: []byte{1, 2},
V: big.NewInt(1),
}
t0.ComputeHash()
t0.Hash()

addr2 := types.StringToAddress("22")
t1 := &types.Transaction{
Expand All @@ -291,7 +291,7 @@ func testBody(t *testing.T, m PlaceholderStorage) {
Input: []byte{4, 5},
V: big.NewInt(2),
}
t1.ComputeHash()
t1.Hash()

block := types.Block{
Header: header,
Expand All @@ -313,7 +313,7 @@ func testBody(t *testing.T, m PlaceholderStorage) {
}

for indx, i := range tx0 {
if i.Hash != tx1[indx].Hash {
if i.Hash() != tx1[indx].Hash() {
t.Fatal("tx not correct")
}
}
Expand Down Expand Up @@ -351,7 +351,7 @@ func testReceipts(t *testing.T, m PlaceholderStorage) {
r0 := &types.Receipt{
Root: types.StringToHash("1"),
CumulativeGasUsed: 10,
TxHash: txn.Hash,
TxHash: txn.Hash(),
LogsBloom: types.Bloom{0x1},
Logs: []*types.Log{
{
Expand All @@ -368,7 +368,7 @@ func testReceipts(t *testing.T, m PlaceholderStorage) {
r1 := &types.Receipt{
Root: types.StringToHash("1"),
CumulativeGasUsed: 10,
TxHash: txn.Hash,
TxHash: txn.Hash(),
LogsBloom: types.Bloom{0x1},
GasUsed: 10,
ContractAddress: &types.Address{0x1},
Expand Down
32 changes: 22 additions & 10 deletions blockchain/testing.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ func NewTestBlockchain(t *testing.T, headers []*types.Header) *Blockchain {
},
}

st := itrie.NewState(itrie.NewMemoryStorage())
st := itrie.NewState(itrie.NewMemoryStorage(), nil)
b, err := newBlockChain(config, state.NewExecutor(config.Params, st, hclog.NewNullLogger()))

if err != nil {
Expand Down Expand Up @@ -277,24 +277,36 @@ func (m *MockVerifier) PreStateCommit(header *types.Header, txn *state.Transitio
return nil
}

func (m *MockVerifier) IsSystemTransaction(height uint64, coinbase types.Address, tx *types.Transaction) bool {
return false
}

func (m *MockVerifier) HookPreStateCommit(fn preStateCommitDelegate) {
m.preStateCommitFn = fn
}

// Executor delegators
type processBlockDelegate func(types.Hash, *types.Block, types.Address) (*state.Transition, error)
type processTransactionDelegate func(*state.Transition, uint64, []*types.Transaction) (*state.Transition, error)

type mockExecutor struct {
processBlockFn processBlockDelegate
processTransactionFn processTransactionDelegate
}

func (m *mockExecutor) ProcessBlock(
func (m *mockExecutor) BeginTxn(
parentRoot types.Hash,
block *types.Block,
blockCreator types.Address,
header *types.Header,
coinbaseReceiver types.Address,
) (*state.Transition, error) {
return &state.Transition{}, nil
}

func (m *mockExecutor) ProcessTransactions(
transition *state.Transition,
gasLimit uint64,
transactions []*types.Transaction,
) (*state.Transition, error) {
if m.processBlockFn != nil {
return m.processBlockFn(parentRoot, block, blockCreator)
if m.processTransactionFn != nil {
return m.processTransactionFn(transition, gasLimit, transactions)
}

return nil, nil
Expand All @@ -304,8 +316,8 @@ func (m *mockExecutor) Stop() {
// do nothing
}

func (m *mockExecutor) HookProcessBlock(fn processBlockDelegate) {
m.processBlockFn = fn
func (m *mockExecutor) HookProcessTransction(fn processTransactionDelegate) {
m.processTransactionFn = fn
}

func TestBlockchain(t *testing.T, genesis *chain.Genesis) *Blockchain {
Expand Down
Loading

0 comments on commit 4205200

Please sign in to comment.