package node
import (
"context"
"fmt"
"github.com/harmony-one/harmony/internal/configs/harmony"
"math/big"
"os"
"strings"
"sync"
"time"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/common"
protobuf "github.com/golang/protobuf/proto"
"github.com/harmony-one/abool"
bls_core "github.com/harmony-one/bls/ffi/go/bls"
lru "github.com/hashicorp/golang-lru"
libp2p_peer "github.com/libp2p/go-libp2p-core/peer"
libp2p_pubsub "github.com/libp2p/go-libp2p-pubsub"
"github.com/pkg/errors"
"github.com/prometheus/client_golang/prometheus"
"github.com/rcrowley/go-metrics"
"golang.org/x/sync/semaphore"
"github.com/harmony-one/harmony/api/proto"
msg_pb "github.com/harmony-one/harmony/api/proto/message"
proto_node "github.com/harmony-one/harmony/api/proto/node"
"github.com/harmony-one/harmony/api/service"
"github.com/harmony-one/harmony/api/service/legacysync"
"github.com/harmony-one/harmony/api/service/legacysync/downloader"
"github.com/harmony-one/harmony/consensus"
"github.com/harmony-one/harmony/core"
"github.com/harmony-one/harmony/core/rawdb"
"github.com/harmony-one/harmony/core/types"
"github.com/harmony-one/harmony/crypto/bls"
"github.com/harmony-one/harmony/internal/chain"
common2 "github.com/harmony-one/harmony/internal/common"
nodeconfig "github.com/harmony-one/harmony/internal/configs/node"
"github.com/harmony-one/harmony/internal/params"
"github.com/harmony-one/harmony/internal/shardchain"
"github.com/harmony-one/harmony/internal/utils"
"github.com/harmony-one/harmony/node/worker"
"github.com/harmony-one/harmony/p2p"
"github.com/harmony-one/harmony/shard"
"github.com/harmony-one/harmony/shard/committee"
"github.com/harmony-one/harmony/staking/reward"
[double-sign] Provide proof of double sign in slash record sent to beaconchain (#2253)
* [double-sign] Commit changes in consensus needed for double-sign
* [double-sign] Leader captures when valdator double signs, broadcasts to beaconchain
* [slash] Add quick iteration tool for testing double-signing
* [slash] Add webhook example
* [slash] Add http server for hook to trigger double sign behavior
* [double-sign] Use bin/trigger-double-sign to cause a double-sign
* [double-sign] Full feedback loop working
* [slash] Thread through the slash records in the block proposal step
* [slash] Compute the slashing rate
* [double-sign] Generalize yaml malicious for many keys
* [double-sign][slash] Modify data structures, verify via webhook handler
* [slash][double-sign] Find one address of bls public key signer, seemingly settle on data structures
* [slash] Apply to state slashing for double signing
* [slash][double-sign] Checkpoint for working code that slashes on beaconchain
* [slash] Keep track of the total slash and total reporters reward
* [slash] Dump account state before and after the slash
* [slash] Satisfy Travis
* [slash][state] Apply slash to the snapshot at beginning of epoch, now need to capture also the new delegates
* [slash] Capture the unique new delegations since snapshot as well
* [slash] Filter undelegation by epoch of double sign
* [slash] Add TODO of correctness needed in slash needs on off-chain data
* [rpc] Fix closure issue on shardID
* [slash] Add delegator to double-sign testing script
* [slash] Expand crt-validator.sh with commenting printfs and make delegation
* [slash] Finish track payment of leftover slash debt after undelegation runs out
* [slash] Now be explicit about error wrt delegatorSlashApply
* [slash] Capture specific sanity check on slash paidoff
* [slash] Track slash from undelegation piecemeal
* [slash][delegation] Named slice types, .String()
* [slash] Do no RLP encode twice, once is enough
* [slash] Remove special case of validators own delegation
* [slash] Refactor approach to slash state application
* [slash] Begin expanding out Verify
* [slash] Slash on snapshot delegations, not current
* [slash] Fix Epoch Cmp
* [slash] Third iteration on slash logic
* [slash] Use full slash amount
* [slash] More log, whitespace
* [slash] Remove Println, add log
* [slash] Remove debug Println
* [slash] Add record in unit test
* [slash] Build Validator snapshot, current. Fill out slash record
* [slash] Need to get RLP dump of a header to use in test
* [slash] Factor out double sign test constants
* [slash] Factor out common for validator, stub out slash application, finish out deserialization setup
* [slash] Factor out data structure creation because of var lexical scoping
* [slash] Seem to have pipeline of unit test e2e executing
* [slash] Add expected snitch, slash amounts
* [slash] Checkpoint
* [slash] Unit test correctly checks case of validator own stake which could drop below 1 ONE in slashing
* [config] add double-sign testnet config (#1)
Signed-off-by: Leo Chen <leo@harmony.one>
* [slash] Commit for as is code & data of current dump.json
* [slash] Order of state operation not correct in test, hence bad results, thank you dlv
* [slash] Add snapshot state dump
* [slash] Pay off slash of validator own delegation correctly
* [slash] Pay off slash debt with special case for min-self
* [slash] Pass first scenario conclusively
* [slash] 2% slash passes unit test for own delegation and external
* [slash] Parameterize unit test to easily test .02 vs .80 slash
* [slash] Handle own delegation correctly at 80% slash
* [slash] Have 80% slash working with external delegator
* [slash] Remove debug code from slash
* [slash] Adjust Apply signature, test again for 2% slash
* [slash] Factor out scenario in testing so can test 2% and 80% at same time
* [slash] Correct balance deduction on plan delegation
* [slash] Mock out ChainReader for TestVerify
* [slash] Small surface area interface, now feedback loop for verify
* [slash] Remove development json
* [slash] trigger-double-sign consumes yaml
* [slash] Remove dead code
* [slash][test] Factor ValidatorWrapper into scenario
* [slash][test] Add example from local-testing dump - caution might be off
* [slash] Factor out mutation of slashDebt
* [slash][test] Factor out tests so can easily load test-case from bytes
* [slash] Fix payment mistake in validator own delegation wrt min-self-delgation respected
* [slash] Satisfy Travis
* [slash] Begin cleanup of PR
* [slash] Apply slash from header to Finalize via state processor
* [slash] Productionize code, Println => logs; adjust slash picked in newblock
* [slash] Need pointer for rlp.Decode
* [slash] ValidatorInformation use full wrapper
* Fix median stake
* [staking] Adjust MarshalJSON for Validator, Wrapper
* Refactor offchain data commit; Make block onchain/offchain commit atomic (#2279)
* Refactor offchain data; Add epoch to ValidatorSnapshot
* Make block onchain/offchain data commit atomically
* [slash][committee] Set .Active to false on double sign, do not consider banned or inactive for committee assignment
* [effective] VC eligible.go
* [consensus] Redundant field in printf
* [docker] import-ks for a dev account
* [slash] Create BLS key for dockerfile and crt-validator.sh
* [slash][docker] Easy deployment of double-sign testing
* [docker] Have slash work as single docker command
* [rpc] Fix median-stake RPC
* [slash] Update webhook with default docker BLS key
* [docker][slash] Fresh yaml copy for docker build, remove dev code in main.go
* [slash] Remove helper binary, commented out code, change to local config
* [params] Factor out test genesis value
* Add shard checking to Tx-Pool & correct blacklist (#2301)
* [core] Fix blacklist & add shardID check
* [staking + node + cmd] Fix blacklist & add shardID check
* [slash] Adjust to PR comments part 1
* [docker] Use different throw away funded account
* [docker] Create easier testing for delegation with private keys
* [docker] Update yaml
* [slash] Remove special case for slashing validator own delegation wrt min-self-delegate
* [docker] Install nano as well
* [slash] Early error if banned
* [quorum] Expose earning account in decider marshal json
* Revert "Refactor offchain data commit; Make block onchain/offchain commit atomic (#2279)"
This reverts commit 9ffbf682c075b49188923c65a0bbf39ac188be00.
* [slash] Add non-sanity check way to update validator
* [reward] Increase percision on percentage in schedule
* [slash] Adjust logs
* [committee] Check eligibility of validator before doing sanity check
* [slash] Update docker
* [slash] Move create validator script to test
* [slash] More log
* [param] Make things faster
* [slash][off-chain] Clear out slashes from pending in writeblockwithstate
* [cross-link] Log is not error, just info
* [blockchain] Not necessary to guard DeletePendingSlashingCandidates
* [slash][consensus] Use plain []byte for signature b/c bls.Sign has private impl fields, rlp does not encode that
* [slash][test] Use faucet as sender, assume user imported
* [slash] Test setup
* [slash] reserve error for real error in logs
* [slash][availability] Apply availability correct, bump signing count each block
* [slash][staking] Consider banned field in sanity check, pay snitch only half of what was actually slashed
* [slash] Pay as much as can
* [slash] use right nowAmt
* [slash] Take away from rewards as well
* [slash] iterate faster
* [slash] Remove dev based timing
* [slash] Add more log, sanity check incoming slash records, only count external for slash rate
* [availability][state] Adjust signature of ValidatorWrapper wrt state, filter out for staked validators, correct availaibility measure on running counters
* [availability] More log
* [slash] Simply pre slash erra slashing
* [slash] Remove development code
* [slash] Use height from recvMsg, todo on epoch
* [staking] Not necessary to touch LastEpochInCommittee in staking_verifier
* [slash] Undo ds in endpoint pattern config
* [slash] Add TODO and log when delegation becomes 0 b/c slash debt payment
* [slash] Abstract staked validators from shard.State into type, set slash rate based BLSKey count
Co-authored-by: Leo Chen <leo@harmony.one>
Co-authored-by: flicker-harmony <52401354+flicker-harmony@users.noreply.github.com>
Co-authored-by: Rongjian Lan <rongjian@harmony.one>
Co-authored-by: Daniel Van Der Maden <daniel@harmony.one>
5 years ago
"github.com/harmony-one/harmony/staking/slash"
staking "github.com/harmony-one/harmony/staking/types"
"github.com/harmony-one/harmony/webhooks"
)
const (
// NumTryBroadCast is the number of times trying to broadcast
NumTryBroadCast = 3
// MsgChanBuffer is the buffer of consensus message handlers.
MsgChanBuffer = 1024
)
const (
maxBroadcastNodes = 10 // broadcast at most maxBroadcastNodes peers that need in sync
broadcastTimeout int64 = 60 * 1000000000 // 1 mins
//SyncIDLength is the length of bytes for syncID
SyncIDLength = 20
)
// use to push new block to outofsync node
type syncConfig struct {
timestamp int64
client * downloader . Client
// Determine to send encoded BlockWithSig or Block
withSig bool
}
// Node represents a protocol-participating node in the network
type Node struct {
Consensus * consensus . Consensus // Consensus object containing all Consensus related data (e.g. committee members, signatures, commits)
BlockChannel chan * types . Block // The channel to send newly proposed blocks
ConfirmedBlockChannel chan * types . Block // The channel to send confirmed blocks
BeaconBlockChannel chan * types . Block // The channel to send beacon blocks for non-beaconchain nodes
pendingCXReceipts map [ string ] * types . CXReceiptsProof // All the receipts received but not yet processed for Consensus
pendingCXMutex sync . Mutex
// Shard databases
shardChains shardchain . Collection
SelfPeer p2p . Peer
// TODO: Neighbors should store only neighbor nodes in the same shard
Neighbors sync . Map // All the neighbor nodes, key is the sha256 of Peer IP/Port, value is the p2p.Peer
stateMutex sync . Mutex // mutex for change node state
// BeaconNeighbors store only neighbor nodes in the beacon chain shard
BeaconNeighbors sync . Map // All the neighbor nodes, key is the sha256 of Peer IP/Port, value is the p2p.Peer
TxPool * core . TxPool
CxPool * core . CxPool // pool for missing cross shard receipts resend
Worker , BeaconWorker * worker . Worker
downloaderServer * downloader . Server
// Syncing component.
syncID [ SyncIDLength ] byte // a unique ID for the node during the state syncing process with peers
stateSync , beaconSync * legacysync . StateSync
peerRegistrationRecord map [ string ] * syncConfig // record registration time (unixtime) of peers begin in syncing
SyncingPeerProvider SyncingPeerProvider
// The p2p host used to send/receive p2p messages
host p2p . Host
// Service manager.
serviceManager * service . Manager
ContractDeployerCurrentNonce uint64 // The nonce of the deployer contract at current block
ContractAddresses [ ] common . Address
// Channel to notify consensus service to really start consensus
startConsensus chan struct { }
HarmonyConfig harmony . HarmonyConfig
// node configuration, including group ID, shard ID, etc
NodeConfig * nodeconfig . ConfigType
// Chain configuration.
chainConfig params . ChainConfig
// map of service type to its message channel.
isFirstTime bool // the node was started with a fresh database
unixTimeAtNodeStart int64
// KeysToAddrs holds the addresses of bls keys run by the node
KeysToAddrs map [ string ] common . Address
keysToAddrsEpoch * big . Int
keysToAddrsMutex sync . Mutex
// TransactionErrorSink contains error messages for any failed transaction, in memory only
TransactionErrorSink * types . TransactionErrorSink
// BroadcastInvalidTx flag is considered when adding pending tx to tx-pool
BroadcastInvalidTx bool
// InSync flag indicates the node is in-sync or not
IsInSync * abool . AtomicBool
proposedBlock map [ uint64 ] * types . Block
deciderCache * lru . Cache
committeeCache * lru . Cache
Metrics metrics . Registry
// context control for pub-sub handling
psCtx context . Context
psCancel func ( )
}
// Blockchain returns the blockchain for the node's current shard.
func ( node * Node ) Blockchain ( ) * core . BlockChain {
shardID := node . NodeConfig . ShardID
bc , err := node . shardChains . ShardChain ( shardID )
if err != nil {
utils . Logger ( ) . Error ( ) .
Uint32 ( "shardID" , shardID ) .
Err ( err ) .
Msg ( "cannot get shard chain" )
}
return bc
}
// Beaconchain returns the beaconchain from node.
func ( node * Node ) Beaconchain ( ) * core . BlockChain {
bc , err := node . shardChains . ShardChain ( shard . BeaconChainShardID )
if err != nil {
utils . Logger ( ) . Error ( ) . Err ( err ) . Msg ( "cannot get beaconchain" )
}
return bc
}
// TODO: make this batch more transactions
func ( node * Node ) tryBroadcast ( tx * types . Transaction ) {
msg := proto_node . ConstructTransactionListMessageAccount ( types . Transactions { tx } )
shardGroupID := nodeconfig . NewGroupIDByShardID ( nodeconfig . ShardID ( tx . ShardID ( ) ) )
utils . Logger ( ) . Info ( ) . Str ( "shardGroupID" , string ( shardGroupID ) ) . Msg ( "tryBroadcast" )
for attempt := 0 ; attempt < NumTryBroadCast ; attempt ++ {
if err := node . host . SendMessageToGroups ( [ ] nodeconfig . GroupID { shardGroupID } ,
p2p . ConstructMessage ( msg ) ) ; err != nil && attempt < NumTryBroadCast {
utils . Logger ( ) . Error ( ) . Int ( "attempt" , attempt ) . Msg ( "Error when trying to broadcast tx" )
} else {
break
}
}
}
func ( node * Node ) tryBroadcastStaking ( stakingTx * staking . StakingTransaction ) {
msg := proto_node . ConstructStakingTransactionListMessageAccount ( staking . StakingTransactions { stakingTx } )
[slash][consensus] Notice double sign & broadcast, factor out tech debt of consensus (#2152)
* [slash] Remove dead interface, associated piping
* [slash] Expand out structs
* [consensus] Write to a chan when find a case of double-signing, remove dead code
* [slash] Broadcast the noticing of a double signing
* [rawdb] CRUD for slashing candidates
* [slashing][node][proto] Broadcast the slash record after receive from consensus, handle received proto message, persist in off-chain db while pending
* [slash][node][propose-block] Add verified slashes proposed into the header in block proposal
* [slash][shard] Factor out external validator as method on shard state, add double-signature field
* [slash][engine] Apply slash, name boolean expression for sorts, use stable sort
* [slash] Abstract Ballot results so keep track of both pre and post double sign event
* [slash] Fix type errors on test code
* [slash] Read from correct rawdb
* [slash] Add epoch based guards in CRUD of slashing
* [slash] Write to correct cache for slashing candidates
* [shard] Use explicit named type of BLS Signature, use convention
* [slash] Fix mistake done in refactor, improper header used. Factor out fromSlice to set
* [slash][node] Restore newblock to master, try again minimial change
* [cx-receipts] Break up one-liner, use SliceStable, not Slice
* [network] Finish refactor that makes network message headers once
* [network] Simplify creation further of headers write
* [slash] Adjust data structure of slash after offline discussion with RJ, Chao
* [slash] Still did need signature of the double signature
* [consensus] Prepare message does not have block header
* [consensus] Soft reset three files to 968517d~1
* [consensus] Begin factor consensus network intended message out with prepare first
* [consensus] Factor out Prepared message
* [consensus] Factor out announce message creation
* [consensus] Committed Message, branch on verify sender key for clearer log
* [consensus] Committed Message Factor out
* [consensus] Do jenkins MVP of signatures adjustment
* [main][slash] Provide YAML config as webhook config for double sign event
* [consensus] Adjust signatures, whitespace, lessen GC pressure
* [consensus] Remove dead code
* [consensus] Factor out commit overloaded message, give commit payload override in construct
* [consensus] Fix travis tests
* [consensus] Provide block bytes in SubmitVote(quorum.Commit)
* [consensus] Factor out noisy sanity checks in BFT, move existing commit check earlier as was before
* [quorum] Adjust signatures in quorum
* [staking] Adjust after merge from master
* [consensus] Finish refactor of consensus
* [node] Fix import
* [consensus] Fix travis
* [consensus] Use origin/master copy of block, fix mistake of pointer to empty byte
* [consensus] Less verbose bools
* [consensus] Remove unused trailing mutation hook in message construct
* [consensus] Address some TODOs on err, comment out double sign
5 years ago
shardGroupID := nodeconfig . NewGroupIDByShardID (
nodeconfig . ShardID ( shard . BeaconChainShardID ) ,
) // broadcast to beacon chain
utils . Logger ( ) . Info ( ) . Str ( "shardGroupID" , string ( shardGroupID ) ) . Msg ( "tryBroadcastStaking" )
for attempt := 0 ; attempt < NumTryBroadCast ; attempt ++ {
if err := node . host . SendMessageToGroups ( [ ] nodeconfig . GroupID { shardGroupID } ,
p2p . ConstructMessage ( msg ) ) ; err != nil && attempt < NumTryBroadCast {
utils . Logger ( ) . Error ( ) . Int ( "attempt" , attempt ) . Msg ( "Error when trying to broadcast staking tx" )
} else {
break
}
}
}
// Add new transactions to the pending transaction list.
func ( node * Node ) addPendingTransactions ( newTxs types . Transactions ) [ ] error {
poolTxs := types . PoolTransactions { }
errs := [ ] error { }
acceptCx := node . Blockchain ( ) . Config ( ) . AcceptsCrossTx ( node . Blockchain ( ) . CurrentHeader ( ) . Epoch ( ) )
for _ , tx := range newTxs {
if tx . ShardID ( ) != tx . ToShardID ( ) && ! acceptCx {
errs = append ( errs , errors . WithMessage ( errInvalidEpoch , "cross-shard tx not accepted yet" ) )
continue
}
if tx . IsEthCompatible ( ) && ! node . Blockchain ( ) . Config ( ) . IsEthCompatible ( node . Blockchain ( ) . CurrentBlock ( ) . Epoch ( ) ) {
errs = append ( errs , errors . WithMessage ( errInvalidEpoch , "ethereum tx not accepted yet" ) )
continue
}
poolTxs = append ( poolTxs , tx )
}
errs = append ( errs , node . TxPool . AddRemotes ( poolTxs ) ... )
pendingCount , queueCount := node . TxPool . Stats ( )
utils . Logger ( ) . Info ( ) .
Interface ( "err" , errs ) .
Int ( "length of newTxs" , len ( newTxs ) ) .
Int ( "totalPending" , pendingCount ) .
Int ( "totalQueued" , queueCount ) .
Msg ( "[addPendingTransactions] Adding more transactions" )
return errs
}
// Add new staking transactions to the pending staking transaction list.
func ( node * Node ) addPendingStakingTransactions ( newStakingTxs staking . StakingTransactions ) [ ] error {
if node . IsRunningBeaconChain ( ) {
if node . Blockchain ( ) . Config ( ) . IsPreStaking ( node . Blockchain ( ) . CurrentHeader ( ) . Epoch ( ) ) {
poolTxs := types . PoolTransactions { }
for _ , tx := range newStakingTxs {
poolTxs = append ( poolTxs , tx )
}
errs := node . TxPool . AddRemotes ( poolTxs )
pendingCount , queueCount := node . TxPool . Stats ( )
utils . Logger ( ) . Info ( ) .
Int ( "length of newStakingTxs" , len ( poolTxs ) ) .
Int ( "totalPending" , pendingCount ) .
Int ( "totalQueued" , queueCount ) .
Msg ( "Got more staking transactions" )
return errs
}
return [ ] error {
errors . WithMessage ( errInvalidEpoch , "staking txs not accepted yet" ) ,
}
}
return [ ] error {
errors . WithMessage ( errInvalidShard , fmt . Sprintf ( "txs only valid on shard %v" , shard . BeaconChainShardID ) ) ,
}
}
// AddPendingStakingTransaction staking transactions
func ( node * Node ) AddPendingStakingTransaction (
newStakingTx * staking . StakingTransaction ,
) error {
if node . IsRunningBeaconChain ( ) {
errs := node . addPendingStakingTransactions ( staking . StakingTransactions { newStakingTx } )
var err error
for i := range errs {
if errs [ i ] != nil {
utils . Logger ( ) . Info ( ) .
Err ( errs [ i ] ) .
Msg ( "[AddPendingStakingTransaction] Failed adding new staking transaction" )
err = errs [ i ]
break
}
}
if err == nil || node . BroadcastInvalidTx {
utils . Logger ( ) . Info ( ) .
Str ( "Hash" , newStakingTx . Hash ( ) . Hex ( ) ) .
Msg ( "Broadcasting Staking Tx" )
node . tryBroadcastStaking ( newStakingTx )
}
return err
}
return nil
}
// AddPendingTransaction adds one new transaction to the pending transaction list.
// This is only called from SDK.
func ( node * Node ) AddPendingTransaction ( newTx * types . Transaction ) error {
if newTx . ShardID ( ) == node . NodeConfig . ShardID {
errs := node . addPendingTransactions ( types . Transactions { newTx } )
var err error
for i := range errs {
if errs [ i ] != nil {
utils . Logger ( ) . Info ( ) . Err ( errs [ i ] ) . Msg ( "[AddPendingTransaction] Failed adding new transaction" )
err = errs [ i ]
break
}
}
if err == nil || node . BroadcastInvalidTx {
utils . Logger ( ) . Info ( ) . Str ( "Hash" , newTx . Hash ( ) . Hex ( ) ) . Str ( "HashByType" , newTx . HashByType ( ) . Hex ( ) ) . Msg ( "Broadcasting Tx" )
node . tryBroadcast ( newTx )
}
return err
}
return errors . Errorf ( "shard do not match, txShard: %d, nodeShard: %d" , newTx . ShardID ( ) , node . NodeConfig . ShardID )
}
// AddPendingReceipts adds one receipt message to pending list.
func ( node * Node ) AddPendingReceipts ( receipts * types . CXReceiptsProof ) {
node . pendingCXMutex . Lock ( )
defer node . pendingCXMutex . Unlock ( )
if receipts . ContainsEmptyField ( ) {
utils . Logger ( ) . Info ( ) .
Int ( "totalPendingReceipts" , len ( node . pendingCXReceipts ) ) .
Msg ( "CXReceiptsProof contains empty field" )
return
}
blockNum := receipts . Header . Number ( ) . Uint64 ( )
shardID := receipts . Header . ShardID ( )
// Sanity checks
if err := node . Blockchain ( ) . Validator ( ) . ValidateCXReceiptsProof ( receipts ) ; err != nil {
if ! strings . Contains ( err . Error ( ) , rawdb . MsgNoShardStateFromDB ) {
utils . Logger ( ) . Error ( ) . Err ( err ) . Msg ( "[AddPendingReceipts] Invalid CXReceiptsProof" )
return
}
}
// cross-shard receipt should not be coming from our shard
if s := node . Consensus . ShardID ; s == shardID {
utils . Logger ( ) . Info ( ) .
Uint32 ( "my-shard" , s ) .
Uint32 ( "receipt-shard" , shardID ) .
Msg ( "ShardID of incoming receipt was same as mine" )
return
}
if e := receipts . Header . Epoch ( ) ; blockNum == 0 ||
! node . Blockchain ( ) . Config ( ) . AcceptsCrossTx ( e ) {
utils . Logger ( ) . Info ( ) .
Uint64 ( "incoming-epoch" , e . Uint64 ( ) ) .
Msg ( "Incoming receipt had meaningless epoch" )
return
}
key := utils . GetPendingCXKey ( shardID , blockNum )
// DDoS protection
const maxCrossTxnSize = 4096
if s := len ( node . pendingCXReceipts ) ; s >= maxCrossTxnSize {
utils . Logger ( ) . Info ( ) .
Int ( "pending-cx-receipts-size" , s ) .
Int ( "pending-cx-receipts-limit" , maxCrossTxnSize ) .
Msg ( "Current pending cx-receipts reached size limit" )
return
}
if _ , ok := node . pendingCXReceipts [ key ] ; ok {
utils . Logger ( ) . Info ( ) .
Int ( "totalPendingReceipts" , len ( node . pendingCXReceipts ) ) .
Msg ( "Already Got Same Receipt message" )
return
}
node . pendingCXReceipts [ key ] = receipts
utils . Logger ( ) . Info ( ) .
Int ( "totalPendingReceipts" , len ( node . pendingCXReceipts ) ) .
Msg ( "Got ONE more receipt message" )
}
type withError struct {
err error
payload interface { }
}
var (
errNotRightKeySize = errors . New ( "key received over wire is wrong size" )
errNoSenderPubKey = errors . New ( "no sender public BLS key in message" )
errWrongSizeOfBitmap = errors . New ( "wrong size of sender bitmap" )
errWrongShardID = errors . New ( "wrong shard id" )
errInvalidNodeMsg = errors . New ( "invalid node message" )
errIgnoreBeaconMsg = errors . New ( "ignore beacon sync block" )
errInvalidEpoch = errors . New ( "invalid epoch for transaction" )
errInvalidShard = errors . New ( "invalid shard" )
)
const beaconBlockHeightTolerance = 2
// validateNodeMessage validate node message
func ( node * Node ) validateNodeMessage ( ctx context . Context , payload [ ] byte ) (
[ ] byte , proto_node . MessageType , error ) {
// length of payload must > p2pNodeMsgPrefixSize
// reject huge node messages
if len ( payload ) >= types . MaxEncodedPoolTransactionSize {
nodeNodeMessageCounterVec . With ( prometheus . Labels { "type" : "invalid_oversized" } ) . Inc ( )
return nil , 0 , core . ErrOversizedData
}
// just ignore payload[0], which is MsgCategoryType (consensus/node)
msgType := proto_node . MessageType ( payload [ proto . MessageCategoryBytes ] )
switch msgType {
case proto_node . Transaction :
// nothing much to validate transaction message unless decode the RLP
nodeNodeMessageCounterVec . With ( prometheus . Labels { "type" : "tx" } ) . Inc ( )
case proto_node . Staking :
// nothing much to validate staking message unless decode the RLP
nodeNodeMessageCounterVec . With ( prometheus . Labels { "type" : "staking_tx" } ) . Inc ( )
case proto_node . Block :
switch proto_node . BlockMessageType ( payload [ p2pNodeMsgPrefixSize ] ) {
case proto_node . Sync :
nodeNodeMessageCounterVec . With ( prometheus . Labels { "type" : "block_sync" } ) . Inc ( )
// checks whether the beacon block is larger than current block number
blocksPayload := payload [ p2pNodeMsgPrefixSize + 1 : ]
var blocks [ ] * types . Block
if err := rlp . DecodeBytes ( blocksPayload , & blocks ) ; err != nil {
return nil , 0 , errors . Wrap ( err , "block decode error" )
}
curBeaconHeight := node . Beaconchain ( ) . CurrentBlock ( ) . NumberU64 ( )
for _ , block := range blocks {
// Ban blocks number that is smaller than tolerance
if block . NumberU64 ( ) + beaconBlockHeightTolerance <= curBeaconHeight {
utils . Logger ( ) . Debug ( ) . Uint64 ( "receivedNum" , block . NumberU64 ( ) ) .
Uint64 ( "currentNum" , curBeaconHeight ) . Msg ( "beacon block sync message rejected" )
return nil , 0 , errors . New ( "beacon block height smaller than current height beyond tolerance" )
} else if block . NumberU64 ( ) <= curBeaconHeight {
utils . Logger ( ) . Debug ( ) . Uint64 ( "receivedNum" , block . NumberU64 ( ) ) .
Uint64 ( "currentNum" , curBeaconHeight ) . Msg ( "beacon block sync message ignored" )
return nil , 0 , errIgnoreBeaconMsg
}
}
// only non-beacon nodes process the beacon block sync messages
if node . Blockchain ( ) . ShardID ( ) == shard . BeaconChainShardID {
return nil , 0 , errIgnoreBeaconMsg
}
case proto_node . SlashCandidate :
nodeNodeMessageCounterVec . With ( prometheus . Labels { "type" : "slash" } ) . Inc ( )
// only beacon chain node process slash candidate messages
if ! node . IsRunningBeaconChain ( ) {
return nil , 0 , errIgnoreBeaconMsg
}
case proto_node . Receipt :
nodeNodeMessageCounterVec . With ( prometheus . Labels { "type" : "node_receipt" } ) . Inc ( )
case proto_node . CrossLink :
nodeNodeMessageCounterVec . With ( prometheus . Labels { "type" : "crosslink" } ) . Inc ( )
// only beacon chain node process crosslink messages
if ! node . IsRunningBeaconChain ( ) ||
node . NodeConfig . Role ( ) == nodeconfig . ExplorerNode {
return nil , 0 , errIgnoreBeaconMsg
}
default :
nodeNodeMessageCounterVec . With ( prometheus . Labels { "type" : "invalid_block_type" } ) . Inc ( )
return nil , 0 , errInvalidNodeMsg
}
default :
nodeNodeMessageCounterVec . With ( prometheus . Labels { "type" : "invalid_node_type" } ) . Inc ( )
return nil , 0 , errInvalidNodeMsg
}
return payload [ p2pNodeMsgPrefixSize : ] , msgType , nil
}
// validateShardBoundMessage validate consensus message
// validate shardID
// validate public key size
// verify message signature
func ( node * Node ) validateShardBoundMessage (
ctx context . Context , payload [ ] byte ,
) ( * msg_pb . Message , * bls . SerializedPublicKey , bool , error ) {
var (
m msg_pb . Message
)
if err := protobuf . Unmarshal ( payload , & m ) ; err != nil {
nodeConsensusMessageCounterVec . With ( prometheus . Labels { "type" : "invalid_unmarshal" } ) . Inc ( )
return nil , nil , true , errors . WithStack ( err )
}
// ignore messages not intended for explorer
if node . NodeConfig . Role ( ) == nodeconfig . ExplorerNode {
switch m . Type {
case
msg_pb . MessageType_ANNOUNCE ,
msg_pb . MessageType_PREPARE ,
msg_pb . MessageType_COMMIT ,
msg_pb . MessageType_VIEWCHANGE ,
msg_pb . MessageType_NEWVIEW :
nodeConsensusMessageCounterVec . With ( prometheus . Labels { "type" : "ignored" } ) . Inc ( )
return nil , nil , true , nil
}
}
// when node is in ViewChanging mode, it still accepts normal messages into FBFTLog
// in order to avoid possible trap forever but drop PREPARE and COMMIT
// which are message types specifically for a node acting as leader
// so we just ignore those messages
if node . Consensus . IsViewChangingMode ( ) {
switch m . Type {
case msg_pb . MessageType_PREPARE , msg_pb . MessageType_COMMIT :
nodeConsensusMessageCounterVec . With ( prometheus . Labels { "type" : "ignored" } ) . Inc ( )
return nil , nil , true , nil
}
} else {
// ignore viewchange/newview message if the node is not in viewchanging mode
switch m . Type {
case msg_pb . MessageType_NEWVIEW , msg_pb . MessageType_VIEWCHANGE :
nodeConsensusMessageCounterVec . With ( prometheus . Labels { "type" : "ignored" } ) . Inc ( )
return nil , nil , true , nil
}
}
// ignore message not intended for leader, but still forward them to the network
if node . Consensus . IsLeader ( ) {
switch m . Type {
case msg_pb . MessageType_ANNOUNCE , msg_pb . MessageType_PREPARED , msg_pb . MessageType_COMMITTED :
nodeConsensusMessageCounterVec . With ( prometheus . Labels { "type" : "ignored" } ) . Inc ( )
return nil , nil , true , nil
}
}
maybeCon , maybeVC := m . GetConsensus ( ) , m . GetViewchange ( )
senderKey := [ ] byte { }
senderBitmap := [ ] byte { }
if maybeCon != nil {
if maybeCon . ShardId != node . Consensus . ShardID {
nodeConsensusMessageCounterVec . With ( prometheus . Labels { "type" : "invalid_shard" } ) . Inc ( )
return nil , nil , true , errors . WithStack ( errWrongShardID )
}
senderKey = maybeCon . SenderPubkey
if len ( maybeCon . SenderPubkeyBitmap ) > 0 {
senderBitmap = maybeCon . SenderPubkeyBitmap
}
} else if maybeVC != nil {
if maybeVC . ShardId != node . Consensus . ShardID {
nodeConsensusMessageCounterVec . With ( prometheus . Labels { "type" : "invalid_shard" } ) . Inc ( )
return nil , nil , true , errors . WithStack ( errWrongShardID )
}
senderKey = maybeVC . SenderPubkey
} else {
nodeConsensusMessageCounterVec . With ( prometheus . Labels { "type" : "invalid" } ) . Inc ( )
return nil , nil , true , errors . WithStack ( errNoSenderPubKey )
}
// ignore mesage not intended for validator
// but still forward them to the network
if ! node . Consensus . IsLeader ( ) {
switch m . Type {
case msg_pb . MessageType_PREPARE , msg_pb . MessageType_COMMIT :
nodeConsensusMessageCounterVec . With ( prometheus . Labels { "type" : "ignored" } ) . Inc ( )
return nil , nil , true , nil
}
}
serializedKey := bls . SerializedPublicKey { }
if len ( senderKey ) > 0 {
if len ( senderKey ) != bls . PublicKeySizeInBytes {
nodeConsensusMessageCounterVec . With ( prometheus . Labels { "type" : "invalid_key_size" } ) . Inc ( )
return nil , nil , true , errors . WithStack ( errNotRightKeySize )
}
copy ( serializedKey [ : ] , senderKey )
if ! node . Consensus . IsValidatorInCommittee ( serializedKey ) {
nodeConsensusMessageCounterVec . With ( prometheus . Labels { "type" : "invalid_committee" } ) . Inc ( )
return nil , nil , true , errors . WithStack ( shard . ErrValidNotInCommittee )
}
} else {
count := node . Consensus . Decider . ParticipantsCount ( )
if ( count + 7 ) >> 3 != int64 ( len ( senderBitmap ) ) {
nodeConsensusMessageCounterVec . With ( prometheus . Labels { "type" : "invalid_participant_count" } ) . Inc ( )
return nil , nil , true , errors . WithStack ( errWrongSizeOfBitmap )
}
}
nodeConsensusMessageCounterVec . With ( prometheus . Labels { "type" : "valid" } ) . Inc ( )
// serializedKey will be empty for multiSig sender
return & m , & serializedKey , false , nil
}
var (
errMsgHadNoHMYPayLoadAssumption = errors . New ( "did not have sufficient size for hmy msg" )
errConsensusMessageOnUnexpectedTopic = errors . New ( "received consensus on wrong topic" )
)
// StartPubSub kicks off the node message handling
func ( node * Node ) StartPubSub ( ) error {
node . psCtx , node . psCancel = context . WithCancel ( context . Background ( ) )
// groupID and whether this topic is used for consensus
type t struct {
tp nodeconfig . GroupID
isCon bool
}
groups := map [ nodeconfig . GroupID ] bool { }
// three topic subscribed by each validator
for _ , t := range [ ] t {
{ node . NodeConfig . GetShardGroupID ( ) , true } ,
{ node . NodeConfig . GetClientGroupID ( ) , false } ,
} {
if _ , ok := groups [ t . tp ] ; ! ok {
groups [ t . tp ] = t . isCon
}
}
type u struct {
p2p . NamedTopic
consensusBound bool
}
var allTopics [ ] u
utils . Logger ( ) . Debug ( ) .
Interface ( "topics-ended-up-with" , groups ) .
Uint32 ( "shard-id" , node . Consensus . ShardID ) .
Msg ( "starting with these topics" )
if ! node . NodeConfig . IsOffline {
for key , isCon := range groups {
topicHandle , err := node . host . GetOrJoin ( string ( key ) )
if err != nil {
return err
}
allTopics = append (
allTopics , u {
NamedTopic : p2p . NamedTopic { string ( key ) , topicHandle } ,
consensusBound : isCon ,
} ,
)
}
}
pubsub := node . host . PubSub ( )
ownID := node . host . GetID ( )
errChan := make ( chan withError , 100 )
// p2p consensus message handler function
type p2pHandlerConsensus func (
ctx context . Context ,
msg * msg_pb . Message ,
key * bls . SerializedPublicKey ,
) error
// other p2p message handler function
type p2pHandlerElse func (
ctx context . Context ,
rlpPayload [ ] byte ,
actionType proto_node . MessageType ,
) error
// interface pass to p2p message validator
type validated struct {
consensusBound bool
handleC p2pHandlerConsensus
handleCArg * msg_pb . Message
handleE p2pHandlerElse
handleEArg [ ] byte
senderPubKey * bls . SerializedPublicKey
actionType proto_node . MessageType
}
isThisNodeAnExplorerNode := node . NodeConfig . Role ( ) == nodeconfig . ExplorerNode
nodeStringCounterVec . WithLabelValues ( "peerid" , nodeconfig . GetPeerID ( ) . String ( ) ) . Inc ( )
for i := range allTopics {
sub , err := allTopics [ i ] . Topic . Subscribe ( )
if err != nil {
return err
}
topicNamed := allTopics [ i ] . Name
isConsensusBound := allTopics [ i ] . consensusBound
utils . Logger ( ) . Info ( ) .
Str ( "topic" , topicNamed ) .
Msg ( "enabled topic validation pubsub messages" )
// register topic validator for each topic
if err := pubsub . RegisterTopicValidator (
topicNamed ,
// this is the validation function called to quickly validate every p2p message
func ( ctx context . Context , peer libp2p_peer . ID , msg * libp2p_pubsub . Message ) libp2p_pubsub . ValidationResult {
nodeP2PMessageCounterVec . With ( prometheus . Labels { "type" : "total" } ) . Inc ( )
hmyMsg := msg . GetData ( )
// first to validate the size of the p2p message
if len ( hmyMsg ) < p2pMsgPrefixSize {
// TODO (lc): block peers sending empty messages
nodeP2PMessageCounterVec . With ( prometheus . Labels { "type" : "invalid_size" } ) . Inc ( )
return libp2p_pubsub . ValidationReject
}
openBox := hmyMsg [ p2pMsgPrefixSize : ]
// validate message category
switch proto . MessageCategory ( openBox [ proto . MessageCategoryBytes - 1 ] ) {
case proto . Consensus :
// received consensus message in non-consensus bound topic
if ! isConsensusBound {
nodeP2PMessageCounterVec . With ( prometheus . Labels { "type" : "invalid_bound" } ) . Inc ( )
errChan <- withError {
errors . WithStack ( errConsensusMessageOnUnexpectedTopic ) , msg ,
}
return libp2p_pubsub . ValidationReject
}
nodeP2PMessageCounterVec . With ( prometheus . Labels { "type" : "consensus_total" } ) . Inc ( )
// validate consensus message
validMsg , senderPubKey , ignore , err := node . validateShardBoundMessage (
context . TODO ( ) , openBox [ proto . MessageCategoryBytes : ] ,
)
if err != nil {
errChan <- withError { err , msg . GetFrom ( ) }
return libp2p_pubsub . ValidationReject
}
// ignore the further processing of the p2p messages as it is not intended for this node
if ignore {
return libp2p_pubsub . ValidationAccept
}
msg . ValidatorData = validated {
consensusBound : true ,
handleC : node . Consensus . HandleMessageUpdate ,
handleCArg : validMsg ,
senderPubKey : senderPubKey ,
}
return libp2p_pubsub . ValidationAccept
case proto . Node :
// node message is almost empty
if len ( openBox ) <= p2pNodeMsgPrefixSize {
nodeP2PMessageCounterVec . With ( prometheus . Labels { "type" : "invalid_size" } ) . Inc ( )
return libp2p_pubsub . ValidationReject
}
nodeP2PMessageCounterVec . With ( prometheus . Labels { "type" : "node_total" } ) . Inc ( )
validMsg , actionType , err := node . validateNodeMessage (
context . TODO ( ) , openBox ,
)
if err != nil {
switch err {
case errIgnoreBeaconMsg :
// ignore the further processing of the ignored messages as it is not intended for this node
// but propogate the messages to other nodes
return libp2p_pubsub . ValidationAccept
default :
// TODO (lc): block peers sending error messages
errChan <- withError { err , msg . GetFrom ( ) }
return libp2p_pubsub . ValidationReject
}
}
msg . ValidatorData = validated {
consensusBound : false ,
handleE : node . HandleNodeMessage ,
handleEArg : validMsg ,
actionType : actionType ,
}
return libp2p_pubsub . ValidationAccept
default :
// ignore garbled messages
nodeP2PMessageCounterVec . With ( prometheus . Labels { "type" : "ignored" } ) . Inc ( )
return libp2p_pubsub . ValidationReject
}
select {
case <- ctx . Done ( ) :
if errors . Is ( ctx . Err ( ) , context . DeadlineExceeded ) ||
errors . Is ( ctx . Err ( ) , context . Canceled ) {
utils . Logger ( ) . Warn ( ) .
Str ( "topic" , topicNamed ) . Msg ( "[context] exceeded validation deadline" )
}
errChan <- withError { errors . WithStack ( ctx . Err ( ) ) , nil }
default :
return libp2p_pubsub . ValidationAccept
}
return libp2p_pubsub . ValidationReject
} ,
// WithValidatorTimeout is an option that sets a timeout for an (asynchronous) topic validator. By default there is no timeout in asynchronous validators.
// TODO: Currently this timeout is useless. Verify me.
libp2p_pubsub . WithValidatorTimeout ( 250 * time . Millisecond ) ,
// WithValidatorConcurrency set the concurernt validator, default is 1024
libp2p_pubsub . WithValidatorConcurrency ( p2p . SetAsideForConsensus ) ,
// WithValidatorInline is an option that sets the validation disposition to synchronous:
// it will be executed inline in validation front-end, without spawning a new goroutine.
// This is suitable for simple or cpu-bound validators that do not block.
libp2p_pubsub . WithValidatorInline ( true ) ,
) ; err != nil {
return err
}
semConsensus := semaphore . NewWeighted ( p2p . SetAsideForConsensus )
msgChanConsensus := make ( chan validated , MsgChanBuffer )
// goroutine to handle consensus messages
go func ( ) {
for {
select {
case <- node . psCtx . Done ( ) :
return
case m := <- msgChanConsensus :
// should not take more than 30 seconds to process one message
ctx , cancel := context . WithTimeout ( node . psCtx , 30 * time . Second )
msg := m
go func ( ) {
defer cancel ( )
if semConsensus . TryAcquire ( 1 ) {
defer semConsensus . Release ( 1 )
if isThisNodeAnExplorerNode {
if err := node . explorerMessageHandler (
ctx , msg . handleCArg ,
) ; err != nil {
errChan <- withError { err , nil }
}
} else {
if err := msg . handleC ( ctx , msg . handleCArg , msg . senderPubKey ) ; err != nil {
errChan <- withError { err , msg . senderPubKey }
}
}
}
select {
// FIXME: wrong use of context. This message have already passed handle actually.
case <- ctx . Done ( ) :
if errors . Is ( ctx . Err ( ) , context . DeadlineExceeded ) ||
errors . Is ( ctx . Err ( ) , context . Canceled ) {
utils . Logger ( ) . Warn ( ) .
Str ( "topic" , topicNamed ) . Msg ( "[context] exceeded consensus message handler deadline" )
}
errChan <- withError { errors . WithStack ( ctx . Err ( ) ) , nil }
default :
return
}
} ( )
}
}
} ( )
semNode := semaphore . NewWeighted ( p2p . SetAsideOtherwise )
msgChanNode := make ( chan validated , MsgChanBuffer )
// goroutine to handle node messages
go func ( ) {
for {
select {
case m := <- msgChanNode :
ctx , cancel := context . WithTimeout ( node . psCtx , 10 * time . Second )
msg := m
go func ( ) {
defer cancel ( )
if semNode . TryAcquire ( 1 ) {
defer semNode . Release ( 1 )
if err := msg . handleE ( ctx , msg . handleEArg , msg . actionType ) ; err != nil {
errChan <- withError { err , nil }
}
}
select {
case <- ctx . Done ( ) :
if errors . Is ( ctx . Err ( ) , context . DeadlineExceeded ) ||
errors . Is ( ctx . Err ( ) , context . Canceled ) {
utils . Logger ( ) . Warn ( ) .
Str ( "topic" , topicNamed ) . Msg ( "[context] exceeded node message handler deadline" )
}
errChan <- withError { errors . WithStack ( ctx . Err ( ) ) , nil }
default :
return
}
} ( )
case <- node . psCtx . Done ( ) :
return
}
}
} ( )
go func ( ) {
for {
nextMsg , err := sub . Next ( node . psCtx )
if err != nil {
if err == context . Canceled {
return
}
errChan <- withError { errors . WithStack ( err ) , nil }
continue
}
if nextMsg . GetFrom ( ) == ownID {
continue
}
if validatedMessage , ok := nextMsg . ValidatorData . ( validated ) ; ok {
if validatedMessage . consensusBound {
msgChanConsensus <- validatedMessage
} else {
msgChanNode <- validatedMessage
}
} else {
// continue if ValidatorData is nil
if nextMsg . ValidatorData == nil {
continue
}
}
}
} ( )
}
go func ( ) {
for {
select {
case <- node . psCtx . Done ( ) :
return
case e := <- errChan :
utils . SampledLogger ( ) . Info ( ) .
Interface ( "item" , e . payload ) .
Msgf ( "[p2p]: issue while handling incoming p2p message: %v" , e . err )
}
}
} ( )
return nil
}
// StopPubSub stops the pubsub handling
func ( node * Node ) StopPubSub ( ) {
if node . psCancel != nil {
node . psCancel ( )
}
}
// GetSyncID returns the syncID of this node
func ( node * Node ) GetSyncID ( ) [ SyncIDLength ] byte {
return node . syncID
}
// New creates a new node.
func New (
host p2p . Host ,
consensusObj * consensus . Consensus ,
chainDBFactory shardchain . DBFactory ,
blacklist map [ common . Address ] struct { } ,
isArchival map [ uint32 ] bool ,
) * Node {
node := Node { }
node . unixTimeAtNodeStart = time . Now ( ) . Unix ( )
node . TransactionErrorSink = types . NewTransactionErrorSink ( )
// Get the node config that's created in the harmony.go program.
if consensusObj != nil {
node . NodeConfig = nodeconfig . GetShardConfig ( consensusObj . ShardID )
} else {
node . NodeConfig = nodeconfig . GetDefaultConfig ( )
}
copy ( node . syncID [ : ] , GenerateRandomString ( SyncIDLength ) )
if host != nil {
node . host = host
node . SelfPeer = host . GetSelfPeer ( )
}
networkType := node . NodeConfig . GetNetworkType ( )
chainConfig := networkType . ChainConfig ( )
node . chainConfig = chainConfig
engine := chain . NewEngine ( )
collection := shardchain . NewCollection (
chainDBFactory , & genesisInitializer { & node } , engine , & chainConfig ,
)
for shardID , archival := range isArchival {
if archival {
collection . DisableCache ( shardID )
}
}
node . shardChains = collection
node . IsInSync = abool . NewBool ( false )
if host != nil && consensusObj != nil {
// Consensus and associated channel to communicate blocks
node . Consensus = consensusObj
// Load the chains.
blockchain := node . Blockchain ( ) // this also sets node.isFirstTime if the DB is fresh
beaconChain := node . Beaconchain ( )
if b1 , b2 := beaconChain == nil , blockchain == nil ; b1 || b2 {
var err error
if b2 {
shardID := node . NodeConfig . ShardID
// HACK get the real error reason
_ , err = node . shardChains . ShardChain ( shardID )
} else {
_ , err = node . shardChains . ShardChain ( shard . BeaconChainShardID )
}
fmt . Fprintf ( os . Stderr , "Cannot initialize node: %v\n" , err )
os . Exit ( - 1 )
}
node . BlockChannel = make ( chan * types . Block )
node . ConfirmedBlockChannel = make ( chan * types . Block )
node . BeaconBlockChannel = make ( chan * types . Block )
txPoolConfig := core . DefaultTxPoolConfig
txPoolConfig . Blacklist = blacklist
txPoolConfig . Journal = fmt . Sprintf ( "%v/%v" , node . NodeConfig . DBDir , txPoolConfig . Journal )
node . TxPool = core . NewTxPool ( txPoolConfig , node . Blockchain ( ) . Config ( ) , blockchain , node . TransactionErrorSink )
node . CxPool = core . NewCxPool ( core . CxPoolSize )
node . Worker = worker . New ( node . Blockchain ( ) . Config ( ) , blockchain , engine )
node . deciderCache , _ = lru . New ( 16 )
node . committeeCache , _ = lru . New ( 16 )
if node . Blockchain ( ) . ShardID ( ) != shard . BeaconChainShardID {
node . BeaconWorker = worker . New (
node . Beaconchain ( ) . Config ( ) , beaconChain , engine ,
)
}
node . pendingCXReceipts = map [ string ] * types . CXReceiptsProof { }
node . proposedBlock = map [ uint64 ] * types . Block { }
node . Consensus . VerifiedNewBlock = make ( chan * types . Block , 1 )
engine . SetBeaconchain ( beaconChain )
// the sequence number is the next block number to be added in consensus protocol, which is
// always one more than current chain header block
node . Consensus . SetBlockNum ( blockchain . CurrentBlock ( ) . NumberU64 ( ) + 1 )
}
utils . Logger ( ) . Info ( ) .
Interface ( "genesis block header" , node . Blockchain ( ) . GetHeaderByNumber ( 0 ) ) .
Msg ( "Genesis block hash" )
// Setup initial state of syncing.
node . peerRegistrationRecord = map [ string ] * syncConfig { }
node . startConsensus = make ( chan struct { } )
[slash][consensus] Notice double sign & broadcast, factor out tech debt of consensus (#2152)
* [slash] Remove dead interface, associated piping
* [slash] Expand out structs
* [consensus] Write to a chan when find a case of double-signing, remove dead code
* [slash] Broadcast the noticing of a double signing
* [rawdb] CRUD for slashing candidates
* [slashing][node][proto] Broadcast the slash record after receive from consensus, handle received proto message, persist in off-chain db while pending
* [slash][node][propose-block] Add verified slashes proposed into the header in block proposal
* [slash][shard] Factor out external validator as method on shard state, add double-signature field
* [slash][engine] Apply slash, name boolean expression for sorts, use stable sort
* [slash] Abstract Ballot results so keep track of both pre and post double sign event
* [slash] Fix type errors on test code
* [slash] Read from correct rawdb
* [slash] Add epoch based guards in CRUD of slashing
* [slash] Write to correct cache for slashing candidates
* [shard] Use explicit named type of BLS Signature, use convention
* [slash] Fix mistake done in refactor, improper header used. Factor out fromSlice to set
* [slash][node] Restore newblock to master, try again minimial change
* [cx-receipts] Break up one-liner, use SliceStable, not Slice
* [network] Finish refactor that makes network message headers once
* [network] Simplify creation further of headers write
* [slash] Adjust data structure of slash after offline discussion with RJ, Chao
* [slash] Still did need signature of the double signature
* [consensus] Prepare message does not have block header
* [consensus] Soft reset three files to 968517d~1
* [consensus] Begin factor consensus network intended message out with prepare first
* [consensus] Factor out Prepared message
* [consensus] Factor out announce message creation
* [consensus] Committed Message, branch on verify sender key for clearer log
* [consensus] Committed Message Factor out
* [consensus] Do jenkins MVP of signatures adjustment
* [main][slash] Provide YAML config as webhook config for double sign event
* [consensus] Adjust signatures, whitespace, lessen GC pressure
* [consensus] Remove dead code
* [consensus] Factor out commit overloaded message, give commit payload override in construct
* [consensus] Fix travis tests
* [consensus] Provide block bytes in SubmitVote(quorum.Commit)
* [consensus] Factor out noisy sanity checks in BFT, move existing commit check earlier as was before
* [quorum] Adjust signatures in quorum
* [staking] Adjust after merge from master
* [consensus] Finish refactor of consensus
* [node] Fix import
* [consensus] Fix travis
* [consensus] Use origin/master copy of block, fix mistake of pointer to empty byte
* [consensus] Less verbose bools
* [consensus] Remove unused trailing mutation hook in message construct
* [consensus] Address some TODOs on err, comment out double sign
5 years ago
// Broadcast double-signers reported by consensus
if node . Consensus != nil {
go func ( ) {
for doubleSign := range node . Consensus . SlashChan {
utils . Logger ( ) . Info ( ) .
RawJSON ( "double-sign-candidate" , [ ] byte ( doubleSign . String ( ) ) ) .
Msg ( "double sign notified by consensus leader" )
// no point to broadcast the slash if we aren't even in the right epoch yet
if ! node . Blockchain ( ) . Config ( ) . IsStaking (
node . Blockchain ( ) . CurrentHeader ( ) . Epoch ( ) ,
) {
return
}
if hooks := node . NodeConfig . WebHooks . Hooks ; hooks != nil {
if s := hooks . Slashing ; s != nil {
url := s . OnNoticeDoubleSign
go func ( ) { webhooks . DoPost ( url , & doubleSign ) } ( )
[double-sign] Provide proof of double sign in slash record sent to beaconchain (#2253)
* [double-sign] Commit changes in consensus needed for double-sign
* [double-sign] Leader captures when valdator double signs, broadcasts to beaconchain
* [slash] Add quick iteration tool for testing double-signing
* [slash] Add webhook example
* [slash] Add http server for hook to trigger double sign behavior
* [double-sign] Use bin/trigger-double-sign to cause a double-sign
* [double-sign] Full feedback loop working
* [slash] Thread through the slash records in the block proposal step
* [slash] Compute the slashing rate
* [double-sign] Generalize yaml malicious for many keys
* [double-sign][slash] Modify data structures, verify via webhook handler
* [slash][double-sign] Find one address of bls public key signer, seemingly settle on data structures
* [slash] Apply to state slashing for double signing
* [slash][double-sign] Checkpoint for working code that slashes on beaconchain
* [slash] Keep track of the total slash and total reporters reward
* [slash] Dump account state before and after the slash
* [slash] Satisfy Travis
* [slash][state] Apply slash to the snapshot at beginning of epoch, now need to capture also the new delegates
* [slash] Capture the unique new delegations since snapshot as well
* [slash] Filter undelegation by epoch of double sign
* [slash] Add TODO of correctness needed in slash needs on off-chain data
* [rpc] Fix closure issue on shardID
* [slash] Add delegator to double-sign testing script
* [slash] Expand crt-validator.sh with commenting printfs and make delegation
* [slash] Finish track payment of leftover slash debt after undelegation runs out
* [slash] Now be explicit about error wrt delegatorSlashApply
* [slash] Capture specific sanity check on slash paidoff
* [slash] Track slash from undelegation piecemeal
* [slash][delegation] Named slice types, .String()
* [slash] Do no RLP encode twice, once is enough
* [slash] Remove special case of validators own delegation
* [slash] Refactor approach to slash state application
* [slash] Begin expanding out Verify
* [slash] Slash on snapshot delegations, not current
* [slash] Fix Epoch Cmp
* [slash] Third iteration on slash logic
* [slash] Use full slash amount
* [slash] More log, whitespace
* [slash] Remove Println, add log
* [slash] Remove debug Println
* [slash] Add record in unit test
* [slash] Build Validator snapshot, current. Fill out slash record
* [slash] Need to get RLP dump of a header to use in test
* [slash] Factor out double sign test constants
* [slash] Factor out common for validator, stub out slash application, finish out deserialization setup
* [slash] Factor out data structure creation because of var lexical scoping
* [slash] Seem to have pipeline of unit test e2e executing
* [slash] Add expected snitch, slash amounts
* [slash] Checkpoint
* [slash] Unit test correctly checks case of validator own stake which could drop below 1 ONE in slashing
* [config] add double-sign testnet config (#1)
Signed-off-by: Leo Chen <leo@harmony.one>
* [slash] Commit for as is code & data of current dump.json
* [slash] Order of state operation not correct in test, hence bad results, thank you dlv
* [slash] Add snapshot state dump
* [slash] Pay off slash of validator own delegation correctly
* [slash] Pay off slash debt with special case for min-self
* [slash] Pass first scenario conclusively
* [slash] 2% slash passes unit test for own delegation and external
* [slash] Parameterize unit test to easily test .02 vs .80 slash
* [slash] Handle own delegation correctly at 80% slash
* [slash] Have 80% slash working with external delegator
* [slash] Remove debug code from slash
* [slash] Adjust Apply signature, test again for 2% slash
* [slash] Factor out scenario in testing so can test 2% and 80% at same time
* [slash] Correct balance deduction on plan delegation
* [slash] Mock out ChainReader for TestVerify
* [slash] Small surface area interface, now feedback loop for verify
* [slash] Remove development json
* [slash] trigger-double-sign consumes yaml
* [slash] Remove dead code
* [slash][test] Factor ValidatorWrapper into scenario
* [slash][test] Add example from local-testing dump - caution might be off
* [slash] Factor out mutation of slashDebt
* [slash][test] Factor out tests so can easily load test-case from bytes
* [slash] Fix payment mistake in validator own delegation wrt min-self-delgation respected
* [slash] Satisfy Travis
* [slash] Begin cleanup of PR
* [slash] Apply slash from header to Finalize via state processor
* [slash] Productionize code, Println => logs; adjust slash picked in newblock
* [slash] Need pointer for rlp.Decode
* [slash] ValidatorInformation use full wrapper
* Fix median stake
* [staking] Adjust MarshalJSON for Validator, Wrapper
* Refactor offchain data commit; Make block onchain/offchain commit atomic (#2279)
* Refactor offchain data; Add epoch to ValidatorSnapshot
* Make block onchain/offchain data commit atomically
* [slash][committee] Set .Active to false on double sign, do not consider banned or inactive for committee assignment
* [effective] VC eligible.go
* [consensus] Redundant field in printf
* [docker] import-ks for a dev account
* [slash] Create BLS key for dockerfile and crt-validator.sh
* [slash][docker] Easy deployment of double-sign testing
* [docker] Have slash work as single docker command
* [rpc] Fix median-stake RPC
* [slash] Update webhook with default docker BLS key
* [docker][slash] Fresh yaml copy for docker build, remove dev code in main.go
* [slash] Remove helper binary, commented out code, change to local config
* [params] Factor out test genesis value
* Add shard checking to Tx-Pool & correct blacklist (#2301)
* [core] Fix blacklist & add shardID check
* [staking + node + cmd] Fix blacklist & add shardID check
* [slash] Adjust to PR comments part 1
* [docker] Use different throw away funded account
* [docker] Create easier testing for delegation with private keys
* [docker] Update yaml
* [slash] Remove special case for slashing validator own delegation wrt min-self-delegate
* [docker] Install nano as well
* [slash] Early error if banned
* [quorum] Expose earning account in decider marshal json
* Revert "Refactor offchain data commit; Make block onchain/offchain commit atomic (#2279)"
This reverts commit 9ffbf682c075b49188923c65a0bbf39ac188be00.
* [slash] Add non-sanity check way to update validator
* [reward] Increase percision on percentage in schedule
* [slash] Adjust logs
* [committee] Check eligibility of validator before doing sanity check
* [slash] Update docker
* [slash] Move create validator script to test
* [slash] More log
* [param] Make things faster
* [slash][off-chain] Clear out slashes from pending in writeblockwithstate
* [cross-link] Log is not error, just info
* [blockchain] Not necessary to guard DeletePendingSlashingCandidates
* [slash][consensus] Use plain []byte for signature b/c bls.Sign has private impl fields, rlp does not encode that
* [slash][test] Use faucet as sender, assume user imported
* [slash] Test setup
* [slash] reserve error for real error in logs
* [slash][availability] Apply availability correct, bump signing count each block
* [slash][staking] Consider banned field in sanity check, pay snitch only half of what was actually slashed
* [slash] Pay as much as can
* [slash] use right nowAmt
* [slash] Take away from rewards as well
* [slash] iterate faster
* [slash] Remove dev based timing
* [slash] Add more log, sanity check incoming slash records, only count external for slash rate
* [availability][state] Adjust signature of ValidatorWrapper wrt state, filter out for staked validators, correct availaibility measure on running counters
* [availability] More log
* [slash] Simply pre slash erra slashing
* [slash] Remove development code
* [slash] Use height from recvMsg, todo on epoch
* [staking] Not necessary to touch LastEpochInCommittee in staking_verifier
* [slash] Undo ds in endpoint pattern config
* [slash] Add TODO and log when delegation becomes 0 b/c slash debt payment
* [slash] Abstract staked validators from shard.State into type, set slash rate based BLSKey count
Co-authored-by: Leo Chen <leo@harmony.one>
Co-authored-by: flicker-harmony <52401354+flicker-harmony@users.noreply.github.com>
Co-authored-by: Rongjian Lan <rongjian@harmony.one>
Co-authored-by: Daniel Van Der Maden <daniel@harmony.one>
5 years ago
}
}
if ! node . IsRunningBeaconChain ( ) {
go node . BroadcastSlash ( & doubleSign )
} else {
records := slash . Records { doubleSign }
if err := node . Blockchain ( ) . AddPendingSlashingCandidates (
records ,
) ; err != nil {
utils . Logger ( ) . Err ( err ) . Msg ( "could not add new slash to ending slashes" )
[double-sign] Provide proof of double sign in slash record sent to beaconchain (#2253)
* [double-sign] Commit changes in consensus needed for double-sign
* [double-sign] Leader captures when valdator double signs, broadcasts to beaconchain
* [slash] Add quick iteration tool for testing double-signing
* [slash] Add webhook example
* [slash] Add http server for hook to trigger double sign behavior
* [double-sign] Use bin/trigger-double-sign to cause a double-sign
* [double-sign] Full feedback loop working
* [slash] Thread through the slash records in the block proposal step
* [slash] Compute the slashing rate
* [double-sign] Generalize yaml malicious for many keys
* [double-sign][slash] Modify data structures, verify via webhook handler
* [slash][double-sign] Find one address of bls public key signer, seemingly settle on data structures
* [slash] Apply to state slashing for double signing
* [slash][double-sign] Checkpoint for working code that slashes on beaconchain
* [slash] Keep track of the total slash and total reporters reward
* [slash] Dump account state before and after the slash
* [slash] Satisfy Travis
* [slash][state] Apply slash to the snapshot at beginning of epoch, now need to capture also the new delegates
* [slash] Capture the unique new delegations since snapshot as well
* [slash] Filter undelegation by epoch of double sign
* [slash] Add TODO of correctness needed in slash needs on off-chain data
* [rpc] Fix closure issue on shardID
* [slash] Add delegator to double-sign testing script
* [slash] Expand crt-validator.sh with commenting printfs and make delegation
* [slash] Finish track payment of leftover slash debt after undelegation runs out
* [slash] Now be explicit about error wrt delegatorSlashApply
* [slash] Capture specific sanity check on slash paidoff
* [slash] Track slash from undelegation piecemeal
* [slash][delegation] Named slice types, .String()
* [slash] Do no RLP encode twice, once is enough
* [slash] Remove special case of validators own delegation
* [slash] Refactor approach to slash state application
* [slash] Begin expanding out Verify
* [slash] Slash on snapshot delegations, not current
* [slash] Fix Epoch Cmp
* [slash] Third iteration on slash logic
* [slash] Use full slash amount
* [slash] More log, whitespace
* [slash] Remove Println, add log
* [slash] Remove debug Println
* [slash] Add record in unit test
* [slash] Build Validator snapshot, current. Fill out slash record
* [slash] Need to get RLP dump of a header to use in test
* [slash] Factor out double sign test constants
* [slash] Factor out common for validator, stub out slash application, finish out deserialization setup
* [slash] Factor out data structure creation because of var lexical scoping
* [slash] Seem to have pipeline of unit test e2e executing
* [slash] Add expected snitch, slash amounts
* [slash] Checkpoint
* [slash] Unit test correctly checks case of validator own stake which could drop below 1 ONE in slashing
* [config] add double-sign testnet config (#1)
Signed-off-by: Leo Chen <leo@harmony.one>
* [slash] Commit for as is code & data of current dump.json
* [slash] Order of state operation not correct in test, hence bad results, thank you dlv
* [slash] Add snapshot state dump
* [slash] Pay off slash of validator own delegation correctly
* [slash] Pay off slash debt with special case for min-self
* [slash] Pass first scenario conclusively
* [slash] 2% slash passes unit test for own delegation and external
* [slash] Parameterize unit test to easily test .02 vs .80 slash
* [slash] Handle own delegation correctly at 80% slash
* [slash] Have 80% slash working with external delegator
* [slash] Remove debug code from slash
* [slash] Adjust Apply signature, test again for 2% slash
* [slash] Factor out scenario in testing so can test 2% and 80% at same time
* [slash] Correct balance deduction on plan delegation
* [slash] Mock out ChainReader for TestVerify
* [slash] Small surface area interface, now feedback loop for verify
* [slash] Remove development json
* [slash] trigger-double-sign consumes yaml
* [slash] Remove dead code
* [slash][test] Factor ValidatorWrapper into scenario
* [slash][test] Add example from local-testing dump - caution might be off
* [slash] Factor out mutation of slashDebt
* [slash][test] Factor out tests so can easily load test-case from bytes
* [slash] Fix payment mistake in validator own delegation wrt min-self-delgation respected
* [slash] Satisfy Travis
* [slash] Begin cleanup of PR
* [slash] Apply slash from header to Finalize via state processor
* [slash] Productionize code, Println => logs; adjust slash picked in newblock
* [slash] Need pointer for rlp.Decode
* [slash] ValidatorInformation use full wrapper
* Fix median stake
* [staking] Adjust MarshalJSON for Validator, Wrapper
* Refactor offchain data commit; Make block onchain/offchain commit atomic (#2279)
* Refactor offchain data; Add epoch to ValidatorSnapshot
* Make block onchain/offchain data commit atomically
* [slash][committee] Set .Active to false on double sign, do not consider banned or inactive for committee assignment
* [effective] VC eligible.go
* [consensus] Redundant field in printf
* [docker] import-ks for a dev account
* [slash] Create BLS key for dockerfile and crt-validator.sh
* [slash][docker] Easy deployment of double-sign testing
* [docker] Have slash work as single docker command
* [rpc] Fix median-stake RPC
* [slash] Update webhook with default docker BLS key
* [docker][slash] Fresh yaml copy for docker build, remove dev code in main.go
* [slash] Remove helper binary, commented out code, change to local config
* [params] Factor out test genesis value
* Add shard checking to Tx-Pool & correct blacklist (#2301)
* [core] Fix blacklist & add shardID check
* [staking + node + cmd] Fix blacklist & add shardID check
* [slash] Adjust to PR comments part 1
* [docker] Use different throw away funded account
* [docker] Create easier testing for delegation with private keys
* [docker] Update yaml
* [slash] Remove special case for slashing validator own delegation wrt min-self-delegate
* [docker] Install nano as well
* [slash] Early error if banned
* [quorum] Expose earning account in decider marshal json
* Revert "Refactor offchain data commit; Make block onchain/offchain commit atomic (#2279)"
This reverts commit 9ffbf682c075b49188923c65a0bbf39ac188be00.
* [slash] Add non-sanity check way to update validator
* [reward] Increase percision on percentage in schedule
* [slash] Adjust logs
* [committee] Check eligibility of validator before doing sanity check
* [slash] Update docker
* [slash] Move create validator script to test
* [slash] More log
* [param] Make things faster
* [slash][off-chain] Clear out slashes from pending in writeblockwithstate
* [cross-link] Log is not error, just info
* [blockchain] Not necessary to guard DeletePendingSlashingCandidates
* [slash][consensus] Use plain []byte for signature b/c bls.Sign has private impl fields, rlp does not encode that
* [slash][test] Use faucet as sender, assume user imported
* [slash] Test setup
* [slash] reserve error for real error in logs
* [slash][availability] Apply availability correct, bump signing count each block
* [slash][staking] Consider banned field in sanity check, pay snitch only half of what was actually slashed
* [slash] Pay as much as can
* [slash] use right nowAmt
* [slash] Take away from rewards as well
* [slash] iterate faster
* [slash] Remove dev based timing
* [slash] Add more log, sanity check incoming slash records, only count external for slash rate
* [availability][state] Adjust signature of ValidatorWrapper wrt state, filter out for staked validators, correct availaibility measure on running counters
* [availability] More log
* [slash] Simply pre slash erra slashing
* [slash] Remove development code
* [slash] Use height from recvMsg, todo on epoch
* [staking] Not necessary to touch LastEpochInCommittee in staking_verifier
* [slash] Undo ds in endpoint pattern config
* [slash] Add TODO and log when delegation becomes 0 b/c slash debt payment
* [slash] Abstract staked validators from shard.State into type, set slash rate based BLSKey count
Co-authored-by: Leo Chen <leo@harmony.one>
Co-authored-by: flicker-harmony <52401354+flicker-harmony@users.noreply.github.com>
Co-authored-by: Rongjian Lan <rongjian@harmony.one>
Co-authored-by: Daniel Van Der Maden <daniel@harmony.one>
5 years ago
}
[slash][consensus] Notice double sign & broadcast, factor out tech debt of consensus (#2152)
* [slash] Remove dead interface, associated piping
* [slash] Expand out structs
* [consensus] Write to a chan when find a case of double-signing, remove dead code
* [slash] Broadcast the noticing of a double signing
* [rawdb] CRUD for slashing candidates
* [slashing][node][proto] Broadcast the slash record after receive from consensus, handle received proto message, persist in off-chain db while pending
* [slash][node][propose-block] Add verified slashes proposed into the header in block proposal
* [slash][shard] Factor out external validator as method on shard state, add double-signature field
* [slash][engine] Apply slash, name boolean expression for sorts, use stable sort
* [slash] Abstract Ballot results so keep track of both pre and post double sign event
* [slash] Fix type errors on test code
* [slash] Read from correct rawdb
* [slash] Add epoch based guards in CRUD of slashing
* [slash] Write to correct cache for slashing candidates
* [shard] Use explicit named type of BLS Signature, use convention
* [slash] Fix mistake done in refactor, improper header used. Factor out fromSlice to set
* [slash][node] Restore newblock to master, try again minimial change
* [cx-receipts] Break up one-liner, use SliceStable, not Slice
* [network] Finish refactor that makes network message headers once
* [network] Simplify creation further of headers write
* [slash] Adjust data structure of slash after offline discussion with RJ, Chao
* [slash] Still did need signature of the double signature
* [consensus] Prepare message does not have block header
* [consensus] Soft reset three files to 968517d~1
* [consensus] Begin factor consensus network intended message out with prepare first
* [consensus] Factor out Prepared message
* [consensus] Factor out announce message creation
* [consensus] Committed Message, branch on verify sender key for clearer log
* [consensus] Committed Message Factor out
* [consensus] Do jenkins MVP of signatures adjustment
* [main][slash] Provide YAML config as webhook config for double sign event
* [consensus] Adjust signatures, whitespace, lessen GC pressure
* [consensus] Remove dead code
* [consensus] Factor out commit overloaded message, give commit payload override in construct
* [consensus] Fix travis tests
* [consensus] Provide block bytes in SubmitVote(quorum.Commit)
* [consensus] Factor out noisy sanity checks in BFT, move existing commit check earlier as was before
* [quorum] Adjust signatures in quorum
* [staking] Adjust after merge from master
* [consensus] Finish refactor of consensus
* [node] Fix import
* [consensus] Fix travis
* [consensus] Use origin/master copy of block, fix mistake of pointer to empty byte
* [consensus] Less verbose bools
* [consensus] Remove unused trailing mutation hook in message construct
* [consensus] Address some TODOs on err, comment out double sign
5 years ago
}
}
} ( )
}
// update reward values now that node is ready
node . updateInitialRewardValues ( )
// init metrics
initMetrics ( )
nodeStringCounterVec . WithLabelValues ( "version" , nodeconfig . GetVersion ( ) ) . Inc ( )
[double-sign] Provide proof of double sign in slash record sent to beaconchain (#2253)
* [double-sign] Commit changes in consensus needed for double-sign
* [double-sign] Leader captures when valdator double signs, broadcasts to beaconchain
* [slash] Add quick iteration tool for testing double-signing
* [slash] Add webhook example
* [slash] Add http server for hook to trigger double sign behavior
* [double-sign] Use bin/trigger-double-sign to cause a double-sign
* [double-sign] Full feedback loop working
* [slash] Thread through the slash records in the block proposal step
* [slash] Compute the slashing rate
* [double-sign] Generalize yaml malicious for many keys
* [double-sign][slash] Modify data structures, verify via webhook handler
* [slash][double-sign] Find one address of bls public key signer, seemingly settle on data structures
* [slash] Apply to state slashing for double signing
* [slash][double-sign] Checkpoint for working code that slashes on beaconchain
* [slash] Keep track of the total slash and total reporters reward
* [slash] Dump account state before and after the slash
* [slash] Satisfy Travis
* [slash][state] Apply slash to the snapshot at beginning of epoch, now need to capture also the new delegates
* [slash] Capture the unique new delegations since snapshot as well
* [slash] Filter undelegation by epoch of double sign
* [slash] Add TODO of correctness needed in slash needs on off-chain data
* [rpc] Fix closure issue on shardID
* [slash] Add delegator to double-sign testing script
* [slash] Expand crt-validator.sh with commenting printfs and make delegation
* [slash] Finish track payment of leftover slash debt after undelegation runs out
* [slash] Now be explicit about error wrt delegatorSlashApply
* [slash] Capture specific sanity check on slash paidoff
* [slash] Track slash from undelegation piecemeal
* [slash][delegation] Named slice types, .String()
* [slash] Do no RLP encode twice, once is enough
* [slash] Remove special case of validators own delegation
* [slash] Refactor approach to slash state application
* [slash] Begin expanding out Verify
* [slash] Slash on snapshot delegations, not current
* [slash] Fix Epoch Cmp
* [slash] Third iteration on slash logic
* [slash] Use full slash amount
* [slash] More log, whitespace
* [slash] Remove Println, add log
* [slash] Remove debug Println
* [slash] Add record in unit test
* [slash] Build Validator snapshot, current. Fill out slash record
* [slash] Need to get RLP dump of a header to use in test
* [slash] Factor out double sign test constants
* [slash] Factor out common for validator, stub out slash application, finish out deserialization setup
* [slash] Factor out data structure creation because of var lexical scoping
* [slash] Seem to have pipeline of unit test e2e executing
* [slash] Add expected snitch, slash amounts
* [slash] Checkpoint
* [slash] Unit test correctly checks case of validator own stake which could drop below 1 ONE in slashing
* [config] add double-sign testnet config (#1)
Signed-off-by: Leo Chen <leo@harmony.one>
* [slash] Commit for as is code & data of current dump.json
* [slash] Order of state operation not correct in test, hence bad results, thank you dlv
* [slash] Add snapshot state dump
* [slash] Pay off slash of validator own delegation correctly
* [slash] Pay off slash debt with special case for min-self
* [slash] Pass first scenario conclusively
* [slash] 2% slash passes unit test for own delegation and external
* [slash] Parameterize unit test to easily test .02 vs .80 slash
* [slash] Handle own delegation correctly at 80% slash
* [slash] Have 80% slash working with external delegator
* [slash] Remove debug code from slash
* [slash] Adjust Apply signature, test again for 2% slash
* [slash] Factor out scenario in testing so can test 2% and 80% at same time
* [slash] Correct balance deduction on plan delegation
* [slash] Mock out ChainReader for TestVerify
* [slash] Small surface area interface, now feedback loop for verify
* [slash] Remove development json
* [slash] trigger-double-sign consumes yaml
* [slash] Remove dead code
* [slash][test] Factor ValidatorWrapper into scenario
* [slash][test] Add example from local-testing dump - caution might be off
* [slash] Factor out mutation of slashDebt
* [slash][test] Factor out tests so can easily load test-case from bytes
* [slash] Fix payment mistake in validator own delegation wrt min-self-delgation respected
* [slash] Satisfy Travis
* [slash] Begin cleanup of PR
* [slash] Apply slash from header to Finalize via state processor
* [slash] Productionize code, Println => logs; adjust slash picked in newblock
* [slash] Need pointer for rlp.Decode
* [slash] ValidatorInformation use full wrapper
* Fix median stake
* [staking] Adjust MarshalJSON for Validator, Wrapper
* Refactor offchain data commit; Make block onchain/offchain commit atomic (#2279)
* Refactor offchain data; Add epoch to ValidatorSnapshot
* Make block onchain/offchain data commit atomically
* [slash][committee] Set .Active to false on double sign, do not consider banned or inactive for committee assignment
* [effective] VC eligible.go
* [consensus] Redundant field in printf
* [docker] import-ks for a dev account
* [slash] Create BLS key for dockerfile and crt-validator.sh
* [slash][docker] Easy deployment of double-sign testing
* [docker] Have slash work as single docker command
* [rpc] Fix median-stake RPC
* [slash] Update webhook with default docker BLS key
* [docker][slash] Fresh yaml copy for docker build, remove dev code in main.go
* [slash] Remove helper binary, commented out code, change to local config
* [params] Factor out test genesis value
* Add shard checking to Tx-Pool & correct blacklist (#2301)
* [core] Fix blacklist & add shardID check
* [staking + node + cmd] Fix blacklist & add shardID check
* [slash] Adjust to PR comments part 1
* [docker] Use different throw away funded account
* [docker] Create easier testing for delegation with private keys
* [docker] Update yaml
* [slash] Remove special case for slashing validator own delegation wrt min-self-delegate
* [docker] Install nano as well
* [slash] Early error if banned
* [quorum] Expose earning account in decider marshal json
* Revert "Refactor offchain data commit; Make block onchain/offchain commit atomic (#2279)"
This reverts commit 9ffbf682c075b49188923c65a0bbf39ac188be00.
* [slash] Add non-sanity check way to update validator
* [reward] Increase percision on percentage in schedule
* [slash] Adjust logs
* [committee] Check eligibility of validator before doing sanity check
* [slash] Update docker
* [slash] Move create validator script to test
* [slash] More log
* [param] Make things faster
* [slash][off-chain] Clear out slashes from pending in writeblockwithstate
* [cross-link] Log is not error, just info
* [blockchain] Not necessary to guard DeletePendingSlashingCandidates
* [slash][consensus] Use plain []byte for signature b/c bls.Sign has private impl fields, rlp does not encode that
* [slash][test] Use faucet as sender, assume user imported
* [slash] Test setup
* [slash] reserve error for real error in logs
* [slash][availability] Apply availability correct, bump signing count each block
* [slash][staking] Consider banned field in sanity check, pay snitch only half of what was actually slashed
* [slash] Pay as much as can
* [slash] use right nowAmt
* [slash] Take away from rewards as well
* [slash] iterate faster
* [slash] Remove dev based timing
* [slash] Add more log, sanity check incoming slash records, only count external for slash rate
* [availability][state] Adjust signature of ValidatorWrapper wrt state, filter out for staked validators, correct availaibility measure on running counters
* [availability] More log
* [slash] Simply pre slash erra slashing
* [slash] Remove development code
* [slash] Use height from recvMsg, todo on epoch
* [staking] Not necessary to touch LastEpochInCommittee in staking_verifier
* [slash] Undo ds in endpoint pattern config
* [slash] Add TODO and log when delegation becomes 0 b/c slash debt payment
* [slash] Abstract staked validators from shard.State into type, set slash rate based BLSKey count
Co-authored-by: Leo Chen <leo@harmony.one>
Co-authored-by: flicker-harmony <52401354+flicker-harmony@users.noreply.github.com>
Co-authored-by: Rongjian Lan <rongjian@harmony.one>
Co-authored-by: Daniel Van Der Maden <daniel@harmony.one>
5 years ago
node . serviceManager = service . NewManager ( )
return & node
}
// updateInitialRewardValues using the node data
func ( node * Node ) updateInitialRewardValues ( ) {
numShards := shard . Schedule . InstanceForEpoch ( node . Beaconchain ( ) . CurrentHeader ( ) . Epoch ( ) ) . NumShards ( )
initTotal := big . NewInt ( 0 )
for i := uint32 ( 0 ) ; i < numShards ; i ++ {
initTotal = new ( big . Int ) . Add ( core . GetInitialFunds ( i ) , initTotal )
}
reward . SetTotalInitialTokens ( initTotal )
}
// InitConsensusWithValidators initialize shard state
// from latest epoch and update committee pub
// keys for consensus
func ( node * Node ) InitConsensusWithValidators ( ) ( err error ) {
if node . Consensus == nil {
utils . Logger ( ) . Error ( ) .
Msg ( "[InitConsensusWithValidators] consenus is nil; Cannot figure out shardID" )
return errors . New (
"[InitConsensusWithValidators] consenus is nil; Cannot figure out shardID" ,
)
}
shardID := node . Consensus . ShardID
blockNum := node . Blockchain ( ) . CurrentBlock ( ) . NumberU64 ( )
node . Consensus . SetMode ( consensus . Listening )
epoch := shard . Schedule . CalcEpochNumber ( blockNum )
utils . Logger ( ) . Info ( ) .
Uint64 ( "blockNum" , blockNum ) .
Uint32 ( "shardID" , shardID ) .
Uint64 ( "epoch" , epoch . Uint64 ( ) ) .
Msg ( "[InitConsensusWithValidators] Try To Get PublicKeys" )
shardState , err := committee . WithStakingEnabled . Compute (
epoch , node . Consensus . Blockchain ,
)
if err != nil {
utils . Logger ( ) . Err ( err ) .
Uint64 ( "blockNum" , blockNum ) .
Uint32 ( "shardID" , shardID ) .
Uint64 ( "epoch" , epoch . Uint64 ( ) ) .
Msg ( "[InitConsensusWithValidators] Failed getting shard state" )
return err
}
subComm , err := shardState . FindCommitteeByID ( shardID )
if err != nil {
utils . Logger ( ) . Err ( err ) .
Interface ( "shardState" , shardState ) .
Msg ( "[InitConsensusWithValidators] Find CommitteeByID" )
return err
}
[rpc][availability][apr] Richer validator information, implement APR, unify EPoS computation, remove fall 2019 tech debt (#2484)
* [rpc][validator] Extend hmy blockchain validator information
* [availability] Optimize bump count
* [staking][validator][rpc] Remove validator stats rpc, fold into validator information, make existing pattern default behavior
* [slash] Reimplement SetDifference
* [reward][engine][network] Remove bad API from fall, begin setup for Per validator awards
* [header] Custom Marshal header for downstream, remove dev code
* [effective][committee] Factor out EPoS round of computation thereby unification in codebase of EPoS
* [unit-test] Fix semantically wrong validator unit tests, punt on maxBLS key wrt tx-pool test
* [reward] Use excellent singleflight package for caching lookup of subcommittees
* [apr][reward] Begin APR package itself, iterate on iterface signatures
* [reward] Handle possible error from singleflight
* [rpc][validator][reward] Adjust RPC committees, singleflight on votingPower, foldStats into Validator Information
* [apr] Stub out computation of APR
* [effective][committee] Upgrade SlotPurchase with named fields, provide marshal
* [effective] Update Tests
* [blockchain] TODO Remove the validators no longer in committee
* [validator][effective] More expressive string representation of eligibilty, ValidatorRPC explicit say if in committee now
* [rpc] Median-stake more semantic meaningful
* [validator] Iterate on semantic meaning of JSON representation
* [offchain] Make validator stats return explicit error
* [availability] Small typo
* [rpc] Quick visual hack until fix delete out kicked out validators
* [offchain] Delete validator from offchain that lost their slot
* [apr] Forgot to update interface signature
* [apr] Mul instead of Div
* [protocol][validator] Fold block reward accum per vaidator into validator-wrapper, off-chain => on-chain
* [votepower] Refactor votepower Roster, simplify aggregation of network wide rosters
* [votepower][shard] Adjust roster, optimize usage of BLSPublicKey as key, use MarshalText trick
* [shard] Granular errors
* [votepower][validator] Unify votepower data structure with off-chain usage
* [votepower][consensus][validator] Further simplify and unify votepower with off-chain, validator stats
* [votepower] Use RJs naming convention group,overall
* [votepower] Remove Println, do keep enforcing order
* [effective][reward] Expand semantics of eligibility as it was overloaded and confusing, evict old voting power computations
* [apr] Adjust json field name
* [votepower] Only aggregate on external validator
* [votepower] Mistake on aggregation, custom presentation network-wide
* [rpc][validator][availability] Remove parameter, take into account empty snapshot
* [apr] Use snapshots from two, one epochs ago. Still have question on header
* [apr] Use GetHeaderByNumber for the header needed for time stamp
* [chain] Evict > 3 epoch old voting power
* [blockchain] Leave Delete Validator snapshot as TODO
* [validator][rpc][effective] Undo changes to Protocol field, use virtual construct at RPC layer for meaning
* [project] Address PR comments
* [committee][rpc] Move +1 to computation of epos round rather than hack mutation
* [reward] Remove entire unnecessary loop, hook on AddReward. Remove unnecessary new big int
* [votepower][rpc][validator] Stick with numeric.Dec for token involved with computation, expose accumulate block-reward in RPC
* [effective][committee] Track the candidates for the EPoS auction, RPC median-stake benefits
* [node] Add hack way to get real error reason of why cannot load shardchain
* [consensus] Expand log on current issue on nil block
* [apr] Do the actual call to compute for validator's APR
* [committee] Wrap SlotOrder with validator address, manifests in median-stake RPC
* [apr] Incorrect error handle order
* [quorum] Remove incorrect compare on bls Key, (typo), remove redundant error check
* [shard] Add log if stakedSlots is 0
* [apr] More sanity check on div by zero, more lenient on error when dont have historical data yet
* [committee] Remove + 1 on seat count
* [apr] Use int64() directly
* [apr] Log when odd empty nil header
* [apr] Do not crash on empty header, figure out later
5 years ago
pubKeys , err := subComm . BLSPublicKeys ( )
if err != nil {
utils . Logger ( ) . Error ( ) .
Uint32 ( "shardID" , shardID ) .
Uint64 ( "blockNum" , blockNum ) .
Msg ( "[InitConsensusWithValidators] PublicKeys is Empty, Cannot update public keys" )
return errors . Wrapf (
err ,
"[InitConsensusWithValidators] PublicKeys is Empty, Cannot update public keys" ,
)
}
for _ , key := range pubKeys {
if node . Consensus . GetPublicKeys ( ) . Contains ( key . Object ) {
utils . Logger ( ) . Info ( ) .
Uint64 ( "blockNum" , blockNum ) .
Int ( "numPubKeys" , len ( pubKeys ) ) .
Str ( "mode" , node . Consensus . Mode ( ) . String ( ) ) .
Msg ( "[InitConsensusWithValidators] Successfully updated public keys" )
node . Consensus . UpdatePublicKeys ( pubKeys )
node . Consensus . SetMode ( consensus . Normal )
return nil
}
}
return nil
}
// AddPeers adds neighbors nodes
func ( node * Node ) AddPeers ( peers [ ] * p2p . Peer ) int {
for _ , p := range peers {
key := fmt . Sprintf ( "%s:%s:%s" , p . IP , p . Port , p . PeerID )
_ , ok := node . Neighbors . LoadOrStore ( key , * p )
if ! ok {
// !ok means new peer is stored
node . host . AddPeer ( p )
continue
}
}
return node . host . GetPeerCount ( )
}
// AddBeaconPeer adds beacon chain neighbors nodes
// Return false means new neighbor peer was added
// Return true means redundant neighbor peer wasn't added
func ( node * Node ) AddBeaconPeer ( p * p2p . Peer ) bool {
key := fmt . Sprintf ( "%s:%s:%s" , p . IP , p . Port , p . PeerID )
_ , ok := node . BeaconNeighbors . LoadOrStore ( key , * p )
return ok
}
func ( node * Node ) initNodeConfiguration ( ) ( service . NodeConfig , chan p2p . Peer , error ) {
chanPeer := make ( chan p2p . Peer )
nodeConfig := service . NodeConfig {
Beacon : nodeconfig . NewGroupIDByShardID ( shard . BeaconChainShardID ) ,
ShardGroupID : node . NodeConfig . GetShardGroupID ( ) ,
Actions : map [ nodeconfig . GroupID ] nodeconfig . ActionType { } ,
}
groups := [ ] nodeconfig . GroupID {
node . NodeConfig . GetShardGroupID ( ) ,
node . NodeConfig . GetClientGroupID ( ) ,
}
// force the side effect of topic join
if err := node . host . SendMessageToGroups ( groups , [ ] byte { } ) ; err != nil {
return nodeConfig , nil , err
}
return nodeConfig , chanPeer , nil
}
// ServiceManager ...
func ( node * Node ) ServiceManager ( ) * service . Manager {
return node . serviceManager
}
// ShutDown gracefully shut down the node server and dump the in-memory blockchain state into DB.
func ( node * Node ) ShutDown ( ) {
if err := node . StopRPC ( ) ; err != nil {
utils . Logger ( ) . Error ( ) . Err ( err ) . Msg ( "failed to stop RPC" )
}
utils . Logger ( ) . Info ( ) . Msg ( "stopping rosetta" )
if err := node . StopRosetta ( ) ; err != nil {
utils . Logger ( ) . Error ( ) . Err ( err ) . Msg ( "failed to stop rosetta" )
}
utils . Logger ( ) . Info ( ) . Msg ( "stopping services" )
if err := node . StopServices ( ) ; err != nil {
utils . Logger ( ) . Error ( ) . Err ( err ) . Msg ( "failed to stop services" )
}
// Currently pubSub need to be stopped after consensus.
utils . Logger ( ) . Info ( ) . Msg ( "stopping pub-sub" )
node . StopPubSub ( )
utils . Logger ( ) . Info ( ) . Msg ( "stopping host" )
if err := node . host . Close ( ) ; err != nil {
utils . Logger ( ) . Error ( ) . Err ( err ) . Msg ( "failed to stop p2p host" )
}
node . Blockchain ( ) . Stop ( )
node . Beaconchain ( ) . Stop ( )
const msg = "Successfully shut down!\n"
utils . Logger ( ) . Print ( msg )
fmt . Print ( msg )
os . Exit ( 0 )
}
func ( node * Node ) populateSelfAddresses ( epoch * big . Int ) {
// reset the self addresses
node . KeysToAddrs = map [ string ] common . Address { }
node . keysToAddrsEpoch = epoch
shardID := node . Consensus . ShardID
shardState , err := node . Consensus . Blockchain . ReadShardState ( epoch )
if err != nil {
utils . Logger ( ) . Error ( ) . Err ( err ) .
Int64 ( "epoch" , epoch . Int64 ( ) ) .
Uint32 ( "shard-id" , shardID ) .
Msg ( "[PopulateSelfAddresses] failed to read shard" )
return
}
committee , err := shardState . FindCommitteeByID ( shardID )
if err != nil {
utils . Logger ( ) . Error ( ) . Err ( err ) .
Int64 ( "epoch" , epoch . Int64 ( ) ) .
Uint32 ( "shard-id" , shardID ) .
Msg ( "[PopulateSelfAddresses] failed to find shard committee" )
return
}
for _ , blskey := range node . Consensus . GetPublicKeys ( ) {
blsStr := blskey . Bytes . Hex ( )
shardkey := bls . FromLibBLSPublicKeyUnsafe ( blskey . Object )
if shardkey == nil {
utils . Logger ( ) . Error ( ) .
Int64 ( "epoch" , epoch . Int64 ( ) ) .
Uint32 ( "shard-id" , shardID ) .
Str ( "blskey" , blsStr ) .
Msg ( "[PopulateSelfAddresses] failed to get shard key from bls key" )
return
}
addr , err := committee . AddressForBLSKey ( * shardkey )
if err != nil {
utils . Logger ( ) . Error ( ) . Err ( err ) .
Int64 ( "epoch" , epoch . Int64 ( ) ) .
Uint32 ( "shard-id" , shardID ) .
Str ( "blskey" , blsStr ) .
Msg ( "[PopulateSelfAddresses] could not find address" )
return
}
node . KeysToAddrs [ blsStr ] = * addr
utils . Logger ( ) . Debug ( ) .
Int64 ( "epoch" , epoch . Int64 ( ) ) .
Uint32 ( "shard-id" , shardID ) .
Str ( "bls-key" , blsStr ) .
Str ( "address" , common2 . MustAddressToBech32 ( * addr ) ) .
Msg ( "[PopulateSelfAddresses]" )
}
}
// GetAddressForBLSKey retrieves the ECDSA address associated with bls key for epoch
func ( node * Node ) GetAddressForBLSKey ( blskey * bls_core . PublicKey , epoch * big . Int ) common . Address {
// populate if first time setting or new epoch
node . keysToAddrsMutex . Lock ( )
defer node . keysToAddrsMutex . Unlock ( )
if node . keysToAddrsEpoch == nil || epoch . Cmp ( node . keysToAddrsEpoch ) != 0 {
node . populateSelfAddresses ( epoch )
}
blsStr := blskey . SerializeToHexStr ( )
addr , ok := node . KeysToAddrs [ blsStr ]
if ! ok {
return common . Address { }
}
return addr
}
// GetAddresses retrieves all ECDSA addresses of the bls keys for epoch
func ( node * Node ) GetAddresses ( epoch * big . Int ) map [ string ] common . Address {
// populate if first time setting or new epoch
node . keysToAddrsMutex . Lock ( )
defer node . keysToAddrsMutex . Unlock ( )
if node . keysToAddrsEpoch == nil || epoch . Cmp ( node . keysToAddrsEpoch ) != 0 {
node . populateSelfAddresses ( epoch )
}
// self addresses map can never be nil
return node . KeysToAddrs
}
// IsRunningBeaconChain returns whether the node is running on beacon chain.
func ( node * Node ) IsRunningBeaconChain ( ) bool {
return node . NodeConfig . ShardID == shard . BeaconChainShardID
}