Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LFM Phase-1 #30

Open
wants to merge 18 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,17 @@
# or operating system, you probably want to add a global ignore instead:
# git config --global core.excludesfile ~/.gitignore_global

mainnet/
node_1/
node_2/
/tmp
*/**/*un~
*/**/*.test
*un~
.DS_Store
*/**/.DS_Store
.ethtest
password.txt
*/**/*tx_database*
*/**/*dapps*
build/_vendor/pkg
Expand Down
1 change: 1 addition & 0 deletions accounts/abi/bind/base.go
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,7 @@ func (c *BoundContract) estimateGasLimit(opts *TransactOpts, contract *common.Ad
Value: value,
Data: input,
}
//@lfm: no need for price adjustment here as only invoked via contract-invoked txs
return c.transactor.EstimateGas(ensureContext(opts.Context), msg)
}

Expand Down
50 changes: 50 additions & 0 deletions consensus/satoshi/abi.go
Original file line number Diff line number Diff line change
Expand Up @@ -1591,3 +1591,53 @@ const candidateHubABI = `
}
]
`

const networkConfigABI = `
[
{
"inputs": [],
"name": "getConfigParams",
"outputs": [
{
"internalType": "uint256",
"name": "meanGasPrice",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "refreshIntervalInBlocks",
"type": "uint256"
},
{
"internalType": "uint256[]",
"name": "gasPriceSteps",
"type": "uint256[]"
},
{
"internalType": "uint256[]",
"name": "gasDiscountedPrices",
"type": "uint256[]"
},
{
"internalType": "address[]",
"name": "destinationAddresses",
"type": "address[]"
},
{
"internalType": "uint256[]",
"name": "destinationGasFactors",
"type": "uint256[]"
}
],
"stateMutability": "pure",
"type": "function"
},
{
"inputs": [],
"name": "init",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
]
`
220 changes: 220 additions & 0 deletions consensus/satoshi/config_cache_loader.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
package satoshi

import (
"context"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/eth/config"
"github.com/ethereum/go-ethereum/eth/gasprice/lfm"
"github.com/ethereum/go-ethereum/internal/ethapi"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rpc"
"math/big"
)

func (p *Satoshi) refreshNetworkConfigCacheIfNeeded(currentBlockNumber uint64) {
networkConfigCache := lfm.GetNetworkConfigCache()
if !timeToRefreshCache(currentBlockNumber, networkConfigCache) {
return
}
meanGasPrice, refreshIntervalInBlocks, gasPriceSteps, gasDiscountedPrices, destinationGasFactors, ok := p.readConfigParamsFromContract()
if !ok {
return
}
// and update the node's config cache
networkConfigCache.UpdateValues(meanGasPrice, refreshIntervalInBlocks, gasPriceSteps, gasDiscountedPrices, destinationGasFactors, currentBlockNumber)
}

func (p *Satoshi) readConfigParamsFromContract() (int, uint64, []uint64, []uint64, map[common.Address]uint64, bool) {
// get raw results from config contract
resultBytes := queryNetworkConfigContract(p.ethAPI, p.networkConfigABI)
if resultBytes == nil {
log.Warn("@lfm LoadNetworkConfigIfNeeded - failed to read contract")
return configParamsError()
}
// unpack and parse results into config values
meanGasPrice, refreshIntervalInBlocks, gasPriceSteps, gasDiscountedPrices, destinationGasFactors, ok := p.parseConfigResults(resultBytes)
if !ok {
log.Warn("@lfm LoadNetworkConfigIfNeeded - failed to parse config results")
return configParamsError()
}
return meanGasPrice, refreshIntervalInBlocks, gasPriceSteps, gasDiscountedPrices, destinationGasFactors, true
}

func (p *Satoshi) parseConfigResults(resultBytes hexutil.Bytes) (int, uint64, []uint64, []uint64, map[common.Address]uint64, bool) {
var unpacked []interface{}
err := p.networkConfigABI.UnpackIntoInterface(&unpacked, networkConfigGetterFunction, resultBytes)
if err != nil {
log.Warn("@lfm LoadNetworkConfigIfNeeded - failed to invoke networkConfig getter function", "error", err)
return configParamsError()
}
meanGasPrice, refreshIntervalInBlocks, gasPriceSteps, gasDiscountedPrices, destinationGasFactors, ok := parseUnpackedResults(unpacked)
if !ok {
log.Warn("@lfm LoadNetworkConfigIfNeeded - failed to parse getter results")
return configParamsError()
}
return meanGasPrice, refreshIntervalInBlocks, gasPriceSteps, gasDiscountedPrices, destinationGasFactors, true
}

func queryNetworkConfigContract(ethAPI *ethapi.PublicBlockChainAPI, networkConfigABI abi.ABI) hexutil.Bytes {
if ethAPI == nil {
log.Warn("@lfm queryNetworkConfigContract error: no satoshi.EthAPI")
return nil
}
// obtain getter's method signature
signature, err := networkConfigABI.Pack(networkConfigGetterFunction)
if err != nil {
log.Warn("@lfm queryNetworkConfigContract error: could not pack getter function", "getterFunc", networkConfigGetterFunction, "error", err)
return nil
}

msg := ethapi.TransactionArgs{
To: &networkConfigContractAddress,
Data: (*hexutil.Bytes)(&signature),
}
latestBlockNrOrHash := rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber)
ctx := context.Background()
// invoke smart-contract getter
resultBytes, err := ethAPI.Call(ctx, msg, latestBlockNrOrHash, nil)
if err != nil {
log.Warn("@lfm queryNetworkConfigContract error: failed to invoke contract function", "error", err)
return nil
}
if resultBytes == nil || len(resultBytes) == 0 {
log.Warn("@lfm queryNetworkConfigContract returned an empty result")
return nil
}
return resultBytes
}

func timeToRefreshCache(currentBlockNumber uint64, networkConfigCache *config.NetworkConfigCache) bool {
if networkConfigCache.NotInitialized() {
return true
}
refreshIntervalInBlocks := networkConfigCache.RefreshIntervalInBlocks()
refreshBlockReached := currentBlockNumber % refreshIntervalInBlocks == 0
if !refreshBlockReached {
return false
}
lastRefreshBlockNumber := networkConfigCache.LastRefreshBlockNumber()
if lastRefreshBlockNumber == currentBlockNumber {
return false // already refreshed
}
return true
}

func parseUnpackedResults(unpacked []interface{}) (int, uint64, []uint64, []uint64, map[common.Address]uint64, bool) {
const configParamCount = 6
count := len(unpacked)
if count != configParamCount {
log.Warn("@lfm parseUnpackedResults - bad returned value count", "count", count)
return configParamsError()
}
meanGasPrice, ok := unpackedToInt(unpacked[0], "meanGasPrice")
if !ok {
log.Warn("@lfm parseUnpackedResults - failed to unpack meanGasPrice")
return configParamsError()
}
refreshInterval, ok := unpackedToUInt(unpacked[1], "refreshInterval")
if !ok {
log.Warn("parseUnpackedResults - failed to unpack refreshInterval")
return configParamsError()
}
gasPriceSteps, ok := unpackedToUIntArray(unpacked[2], "gasPriceSteps")
if !ok {
log.Warn("parseUnpackedResults - failed to unpack gasPriceSteps")
return configParamsError()
}
gasDiscountedPrices, ok := unpackedToUIntArray(unpacked[3], "gasDiscountedPrices")
if !ok {
log.Warn("parseUnpackedResults - failed to unpack gasDiscountedPrices")
return configParamsError()
}
if len(gasPriceSteps) != len(gasDiscountedPrices) {
log.Warn("parseUnpackedResults - gas price steps and discounted lengths for native CORE transfer don't match")
return configParamsError()
}
destinationAddresses, ok := unpacked[4].([]common.Address)
if !ok {
log.Warn("parseUnpackedResults - failed to unpack destinationAddresses")
return configParamsError()
}
destinationGasFactors, ok := unpackedToUIntArray(unpacked[5], "destinationGasFactors")
if !ok {
log.Warn("parseUnpackedResults - failed to unpack destinationGasFactors")
return configParamsError()
}
if len(destinationAddresses) != len(destinationGasFactors) {
log.Warn("parseUnpackedResults - destination addresses and gasFactor lengths don't match")
return configParamsError()
}
// params are fine
destinationGasFactorMap := createDestinationGasFactorMap(destinationAddresses, destinationGasFactors)
return meanGasPrice, refreshInterval, gasPriceSteps, gasDiscountedPrices, destinationGasFactorMap, true
}

func unpackedToUIntArray(unpacked interface{}, name string) ([]uint64, bool) {
gasPriceStepsBig, ok := unpacked.([]*big.Int)
if !ok {
log.Warn("unpackedToUIntArray - result not BigInt array", "name", name)
return nil, false
}
gasPriceSteps, ok := convertBigToUIntArray(gasPriceStepsBig)
if !ok {
log.Warn("unpackedToUIntArray - failed to cast array to uint array","name", name)
return nil, false
}
return gasPriceSteps, true
}

func unpackedToInt(unpacked interface{}, name string) (int, bool) {
bigInt, ok := unpacked.(*big.Int)
if !ok {
log.Warn("@lfm unpackedToInt - bad parameter type - expected BigInt", "name", name)
return 0, false
}
if !bigInt.IsInt64() {
log.Warn("@lfm unpackedToInt - not a valid int64 value", "name", name)
return 0, false
}
return int(bigInt.Int64()), true
}

func unpackedToUInt(unpacked interface{}, name string) (uint64, bool) {
bigInt, ok := unpacked.(*big.Int)
if !ok {
log.Warn("@lfm unpackedToUInt - bad parameter type - expected BigInt", "name", name)
return 0, false
}
if !bigInt.IsUint64() {
log.Warn("@lfm unpackedToUInt - not a valid uint64 value", "name", name)
return 0, false
}
return bigInt.Uint64(), true
}

func createDestinationGasFactorMap(addresses []common.Address, factors []uint64) map[common.Address]uint64 {
gasFactors := make(map[common.Address]uint64)
for i := 0; i < len(addresses); i++ {
gasFactors[addresses[i]] = factors[i]
}
return gasFactors
}

func convertBigToUIntArray(bigIntArr []*big.Int) ([]uint64, bool) {
uint64Slice := make([]uint64, len(bigIntArr))
for i, bigInt := range bigIntArr {
if !bigInt.IsUint64() {
log.Warn("value at index %d is too large for uint64", "index", i, "value", bigInt)
return nil, false
}
uint64Slice[i] = bigInt.Uint64()
}
return uint64Slice, true
}

func configParamsError() (int, uint64, []uint64, []uint64, map[common.Address]uint64, bool) {
return 0, 0, nil, nil, nil, false
}

16 changes: 15 additions & 1 deletion consensus/satoshi/satoshi.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ const (
wiggleTime = uint64(1) // second, Random delay (per signer) to allow concurrent signers
initialBackOffTime = uint64(1) // second
processBackOffTime = uint64(1) // second
networkConfigGetterFunction = "getConfigParams()"
)

var (
Expand All @@ -77,7 +78,10 @@ var (
common.HexToAddress(systemcontracts.PledgeCandidateContract): true,
common.HexToAddress(systemcontracts.BurnContract): true,
common.HexToAddress(systemcontracts.FoundationContract): true,
common.HexToAddress(systemcontracts.NetworkConfigContract): true,
}

networkConfigContractAddress = common.HexToAddress(systemcontracts.NetworkConfigContract)
)

// Various error messages to mark blocks invalid. These should be private to
Expand Down Expand Up @@ -210,6 +214,7 @@ type Satoshi struct {
validatorSetABI abi.ABI
slashABI abi.ABI
candidateHubABI abi.ABI
networkConfigABI abi.ABI

// The fields below are for testing only
fakeDiff bool // Skip difficulty verifications
Expand Down Expand Up @@ -256,6 +261,10 @@ func New(
if err != nil {
panic(err)
}
cfgABI, err := abi.JSON(strings.NewReader(networkConfigABI))
if err != nil {
panic(err)
}
c := &Satoshi{
chainConfig: chainConfig,
config: satoshiConfig,
Expand All @@ -267,9 +276,9 @@ func New(
validatorSetABI: vABI,
slashABI: sABI,
candidateHubABI: cABI,
networkConfigABI: cfgABI,
signer: types.NewEIP155Signer(chainConfig.ChainID),
}

return c
}

Expand Down Expand Up @@ -678,6 +687,10 @@ func (p *Satoshi) BeforeValidateTx(chain consensus.ChainHeaderReader, header *ty
func (p *Satoshi) BeforePackTx(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB,
txs *[]*types.Transaction, uncles []*types.Header, receipts *[]*types.Receipt) (err error) {
cx := chainContext{Chain: chain, satoshi: p}

// periodically load network-config values into node's cache
p.refreshNetworkConfigCacheIfNeeded(header.Number.Uint64())

// If the block is the last one in a round, execute turn round to update the validator set.
if p.isRoundEnd(chain, header) {
// try turnRound
Expand Down Expand Up @@ -1179,6 +1192,7 @@ func (p *Satoshi) initContract(state *state.StateDB, header *types.Header, chain
systemcontracts.GovHubContract,
systemcontracts.PledgeCandidateContract,
systemcontracts.BurnContract,
systemcontracts.NetworkConfigContract,
}

// get packed data
Expand Down
6 changes: 3 additions & 3 deletions core/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -1459,8 +1459,8 @@ func (bc *BlockChain) writeKnownBlock(block *types.Block) error {
return nil
}

// writeBlockWithState writes block, metadata and corresponding state data to the
// database.
// writeBlockWithState writes block, metadata and corresponding state data to the database
// @lfm commit a new block
func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.Receipt, logs []*types.Log, state *state.StateDB) error {
// Calculate the total difficulty of the block
ptd := bc.GetTd(block.ParentHash(), block.NumberU64()-1)
Expand Down Expand Up @@ -1599,7 +1599,7 @@ func (bc *BlockChain) WriteBlockAndSetHead(block *types.Block, receipts []*types
return bc.writeBlockAndSetHead(block, receipts, logs, state, emitHeadEvent)
}

// writeBlockAndSetHead writes the block and all associated state to the database,
// writeBlockAndSetHead writes the block and all associated state to the database, @lfm
// and also it applies the given block as the new chain head. This function expects
// the chain mutex to be held.
func (bc *BlockChain) writeBlockAndSetHead(block *types.Block, receipts []*types.Receipt, logs []*types.Log, state *state.StateDB, emitHeadEvent bool) (status WriteStatus, err error) {
Expand Down
1 change: 1 addition & 0 deletions core/systemcontracts/const.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ const (
PledgeCandidateContract = "0x0000000000000000000000000000000000001007"
BurnContract = "0x0000000000000000000000000000000000001008"
FoundationContract = "0x0000000000000000000000000000000000001009"
NetworkConfigContract = "0x0000000000000000000000000000000000001016"
)
10 changes: 10 additions & 0 deletions core/systemcontracts/upgrade.go

Large diffs are not rendered by default.

Loading