diff --git a/CLA b/CLA index 5dd2b65ca1..6e17c9eb63 100644 --- a/CLA +++ b/CLA @@ -46,3 +46,4 @@ Tor Hogne Paulsen Tor Hogne Steven Masley Emyrk Factom Inc. Michael Lam factablesolutions Factable Solutions LLC Valentin Ganev sanchopansa Factomatic LLC +Nolan Bauer TRGG3R TRGG3R, LLC; VBIF Factom Protocol Guide diff --git a/VERSION b/VERSION index 49df80bfeb..f22d756da3 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -6.4.4 +6.5.0 diff --git a/common/constants/constants.go b/common/constants/constants.go index 931722f886..0677c9d657 100644 --- a/common/constants/constants.go +++ b/common/constants/constants.go @@ -484,3 +484,4 @@ const ( //Fast boot save state version (savestate) //To be increased whenever the data being saved changes from the last version const SaveStateVersion = 13 +const PreBootWindow = 20 // allow an N minute window before boot where messages will be accepted diff --git a/engine/NetworkProcessorNet.go b/engine/NetworkProcessorNet.go index f89f60824e..e4d41bcda3 100644 --- a/engine/NetworkProcessorNet.go +++ b/engine/NetworkProcessorNet.go @@ -27,40 +27,6 @@ func NetworkProcessorNet(fnode *FactomNode) { } func Peers(fnode *FactomNode) { - saltReplayFilterOn := true - - crossBootIgnore := func(amsg interfaces.IMsg) bool { - // If we are not syncing, we may ignore some old messages if we are rebooting based on salts - if saltReplayFilterOn { - //var ack *messages.Ack - //switch amsg.Type() { - //case constants.MISSING_MSG_RESPONSE: - // mmrsp := amsg.(*messages.MissingMsgResponse) - // if mmrsp.Ack == nil { - // return false - // } - // ack = mmrsp.Ack.(*messages.Ack) - //case constants.ACK_MSG: - // ack = amsg.(*messages.Ack) - //case constants.DIRECTORY_BLOCK_SIGNATURE_MSG: - // dbs := amsg.(*messages.DirectoryBlockSignature) - // if dbs.Ack == nil { - // return false - // } - // ack = dbs.Ack.(*messages.Ack) - //} - - if amsg.Type() == constants.ACK_MSG && amsg != nil { - ack := amsg.(*messages.Ack) - if replaySalt := fnode.State.CrossReplay.ExistOldSalt(ack.Salt); replaySalt { - return true - } - } - - } - - return false - } // ackHeight is used in ignoreMsg to determine if we should ignore an acknowledgment ackHeight := uint32(0) @@ -120,9 +86,6 @@ func Peers(fnode *FactomNode) { for { now := fnode.State.GetTimestamp() - if now.GetTimeSeconds()-fnode.State.BootTime > int64(constants.CROSSBOOT_SALT_REPLAY_DURATION.Seconds()) { - saltReplayFilterOn = false - } cnt := 0 for i := 0; i < 100 && fnode.State.APIQueue().Length() > 0; i++ { @@ -214,7 +177,6 @@ func Peers(fnode *FactomNode) { if fnode.State.LLeaderHeight < fnode.State.DBHeightAtBoot+2 { s := fnode.State - // Allow 20 minute grace period if s.GetMessageFilterTimestamp() != nil && msg.GetTimestamp().GetTimeMilli() < s.GetMessageFilterTimestamp().GetTimeMilli() { fnode.State.LogMessage("NetworkInputs", "Drop, too old", msg) continue @@ -250,12 +212,6 @@ func Peers(fnode *FactomNode) { hash := repeatHash.Fixed() timestamp := msg.GetTimestamp() - tsv := fnode.State.Replay.IsTSValidAndUpdateState(constants.TIME_TEST, hash, timestamp, now) - if !tsv { - fnode.State.LogMessage("NetworkInputs", fromPeer+" Drop, TS invalid", msg) - continue - } - ignore := ignoreMsg(msg) if ignore { fnode.State.LogMessage("NetworkInputs", fromPeer+" Drop, ignoreMsg()", msg) @@ -270,14 +226,6 @@ func Peers(fnode *FactomNode) { // continue //} - rv := fnode.State.Replay.IsTSValidAndUpdateState(constants.NETWORK_REPLAY, hash, timestamp, now) - if !rv { - fnode.State.LogMessage("NetworkInputs", fromPeer+" Drop, NETWORK_REPLAY", msg) - RepeatMsgs.Inc() - //fnode.MLog.add2(fnode, false, peer.GetNameTo(), "PeerIn", false, msg) - continue - } - regex, _ := fnode.State.GetInputRegEx() if regex != nil { @@ -327,10 +275,17 @@ func Peers(fnode *FactomNode) { msg.SetNoResend(true) } - msg.SetNetwork(true) - if !crossBootIgnore(msg) { - sendToExecute(msg, fnode, fromPeer) + // This should be the last check before sendtoexecute because it adds the message to the replay + // as a side effect + rv := fnode.State.Replay.IsTSValidAndUpdateState(constants.NETWORK_REPLAY, hash, timestamp, now) + if !rv { + fnode.State.LogMessage("NetworkInputs", fromPeer+" Drop, NETWORK_REPLAY", msg) + RepeatMsgs.Inc() + //fnode.MLog.add2(fnode, false, peer.GetNameTo(), "PeerIn", false, msg) + continue } + msg.SetNetwork(true) + sendToExecute(msg, fnode, fromPeer) } // For a peer read up to 100 messages {...} } // for each peer {...} if cnt == 0 { diff --git a/engine/factomd.go b/engine/factomd.go index 4a2fe98276..70f7184aeb 100644 --- a/engine/factomd.go +++ b/engine/factomd.go @@ -8,6 +8,7 @@ import ( "fmt" "runtime" + "github.com/FactomProject/factomd/common/constants" "github.com/FactomProject/factomd/common/constants/runstate" . "github.com/FactomProject/factomd/common/globals" "github.com/FactomProject/factomd/common/interfaces" @@ -54,7 +55,7 @@ func Factomd(params *FactomParams, listenToStdin bool) interfaces.IState { state0.SetLeaderTimestamp(state0.TimestampAtBoot) // build a timestamp 20 minutes before boot so we will accept messages from nodes who booted before us. preBootTime := new(primitives.Timestamp) - preBootTime.SetTimeMilli(state0.TimestampAtBoot.GetTimeMilli() - 20*60*1000) + preBootTime.SetTimeMilli(state0.TimestampAtBoot.GetTimeMilli() - constants.PreBootWindow*60*1000) state0.SetMessageFilterTimestamp(preBootTime) state0.EFactory = new(electionMsgs.ElectionsFactory) diff --git a/state/HoldingList.go b/state/HoldingList.go index 44fa0a9f21..b25a0bce94 100644 --- a/state/HoldingList.go +++ b/state/HoldingList.go @@ -155,7 +155,7 @@ func (l *HoldingList) isMsgStale(msg interfaces.IMsg) (res bool) { // l.s.LogMessage("DependentHolding", "SKIP_DBHT_REVIEW", msg) } - if msg.GetTimestamp().GetTime().UnixNano() < l.s.GetFilterTimeNano() { + if msg.GetTimestamp().GetTime().UnixNano() < l.s.GetMessageFilterTimestamp().GetTime().UnixNano() { res = true } diff --git a/state/dbStateCatchup.go b/state/dbStateCatchup.go index 36fae2221e..4a34e43363 100644 --- a/state/dbStateCatchup.go +++ b/state/dbStateCatchup.go @@ -43,14 +43,7 @@ func (list *DBStateList) Catchup() { factomSecond := list.State.FactomSecond() - requestTimeout := list.State.RequestTimeout - if requestTimeout < 1*time.Second { // If the timeout is 0 (default), base off blktime - // 10min block == 30s timeout for a request. - // 5min block == 15s timeout for a request. - // 1min block == 3s timeout for a request. - requestTimeout = factomSecond * 5 - list.State.RequestTimeout = requestTimeout - } + requestTimeout := time.Duration(list.State.RequestTimeout) * factomSecond requestLimit := list.State.RequestLimit // Wait for db to be loaded diff --git a/state/grants.go b/state/grants.go index 773f0be002..f2fd4e3910 100644 --- a/state/grants.go +++ b/state/grants.go @@ -24,9 +24,9 @@ func GetHardCodedGrants() []HardGrant { case "LOCAL": hardcodegrants = []HardGrant{ // waiting for "real-ish" data from brian - HardGrant{21, 2, validateAddress("FA3oajkmHMfqkNMMShmqpwDThzMCuVrSsBwiXM2kYFVRz3MzxNAJ")}, // Pay Clay 2 - HardGrant{31, 4, validateAddress("FA3Ga2XcaheS5NgQ3q22gBpLgE6tXmPu1GhjdU2FsdN2QPMzKJET")}, // Pay Bob 4 - HardGrant{21, 3, validateAddress("FA3Ga2XcaheS5NgQ3q22gBpLgE6tXmPu1GhjdU2FsdN2QPMzKJET")}, // Pay Bob 3 + HardGrant{41, 2, validateAddress("FA3oajkmHMfqkNMMShmqpwDThzMCuVrSsBwiXM2kYFVRz3MzxNAJ")}, // Pay Clay 2 + HardGrant{51, 4, validateAddress("FA3Ga2XcaheS5NgQ3q22gBpLgE6tXmPu1GhjdU2FsdN2QPMzKJET")}, // Pay Bob 4 + HardGrant{41, 3, validateAddress("FA3Ga2XcaheS5NgQ3q22gBpLgE6tXmPu1GhjdU2FsdN2QPMzKJET")}, // Pay Bob 3 // Note to future grant implementers. To test the grants that you have coded up on mainnet before deployment on your local machine use this procedure. // - Code all the grants and add them to the MAIN section. Use the correct activation height, where Height % 25 = 1 @@ -43,17 +43,24 @@ func GetHardCodedGrants() []HardGrant { // - Check you are on the second node by pressing s to print out the summary. It should show "1 f FNode01" to indicate focus is on the new simnode. This means you are viewing that control panel now. // - Refersh the control panel and make sure that it downloads the blockchain and is keeping up with the first simnode and is not stalled on either of the grant blocks. + // - Note, if set to 11, then there may be an extra payout at block 151 + // the TESTNET_COINBASE_PERIOD has a activation height of 25 for local net instead of maxint so after block 25 the + // COINBASE_DECLARATION is changed to 140 so at height 151 we look back 140 blocks and get the grants at height 11 + // and pay them out a second time. Perhaps we should handle that differently somehow on any future activation + // heights that change the COINBASE constants. The bug is TESTNET_COINBASE_PERIOD should never activate for local + // networks since it is intended to only apply to test nets. + // Copy (and replace) the new grants to be tested here: // Centis BV total: 1200 FCT (300 FCT * 2 months) + (600 FCT * 1 month) = 1200 FCT lowered upon request - HardGrant{11, 1200e8, validateAddress("FA2hvRaci9Kks9cLNkEUFcxzUJuUFaaAE1eWYLqa2qk1k9pVFVBp")}, + HardGrant{31, 1200e8, validateAddress("FA2hvRaci9Kks9cLNkEUFcxzUJuUFaaAE1eWYLqa2qk1k9pVFVBp")}, // The 42nd Factoid total: 1800 FCT - HardGrant{11, 1800e8, validateAddress("FA3AEL2H9XZy3n199USs2poCEJBkK1Egy6JXhLehfLJjUYMKh1zS")}, + HardGrant{31, 1800e8, validateAddress("FA3AEL2H9XZy3n199USs2poCEJBkK1Egy6JXhLehfLJjUYMKh1zS")}, // Factom, Inc. total: 1800 FCT - HardGrant{11, 1800e8, validateAddress("FA2teRURMYTdYAA97zdh7rZDkxNtR1nhjryo34aaskjYqsqRSwZq")}, + HardGrant{31, 1800e8, validateAddress("FA2teRURMYTdYAA97zdh7rZDkxNtR1nhjryo34aaskjYqsqRSwZq")}, // Canonical Ledgers total: 1800 FCT - HardGrant{11, 1800e8, validateAddress("FA2PEXgRiPd14NzUP47XfVTgEnvjtLSebBZvnM8gM7cJAMuqWs89")}, + HardGrant{31, 1800e8, validateAddress("FA2PEXgRiPd14NzUP47XfVTgEnvjtLSebBZvnM8gM7cJAMuqWs89")}, // DBGrow total: 1800 FCT - HardGrant{11, 1800e8, validateAddress("FA3HSuFo9Soa5ZnG82JHqyKiRi4Pw17LxPTo9AsCaFNLCGkXkgsu")}, + HardGrant{31, 1800e8, validateAddress("FA3HSuFo9Soa5ZnG82JHqyKiRi4Pw17LxPTo9AsCaFNLCGkXkgsu")}, } case "CUSTOM": hardcodegrants = []HardGrant{} @@ -788,6 +795,184 @@ func GetHardCodedGrants() []HardGrant { // Kompendium total: 2400 FCT HardGrant{207326, 2400e8, validateAddress("FA3KMPNX8AKdY3tjyKMzZ5cAkqUv97d3QqkJeQnVXk6PXSVgArnr")}, // -------------------------------------------------------- + + // ******************************** + // **** Grant Round 2019-04 **** + // ******************************** + + // -------------------------------------------------------- + // Samuel Vanderwaal-Brian Deery-Nolan Bauer-Nic R-The 42nd Factoid AS-Centis BV -- 9000 FCT + // Guide Compensation 2019-09-07 to 2019-12-07 + + // Factom, Inc. total: 1800 FCT + HardGrant{221126, 1800e8, validateAddress("FA2teRURMYTdYAA97zdh7rZDkxNtR1nhjryo34aaskjYqsqRSwZq")}, + + // Nolan Bauer total: 1800 FCT + HardGrant{221126, 1800e8, validateAddress("FA2oecgJW3XWnXzHhQQoULmMeKC97uAgHcPd4kEowTb3csVkbDc9")}, + + // Nic R total: 1800 FCT + HardGrant{221126, 1800e8, validateAddress("FA3HSuFo9Soa5ZnG82JHqyKiRi4Pw17LxPTo9AsCaFNLCGkXkgsu")}, + + // The 42nd Factoid AS total: 1800 FCT + HardGrant{221126, 1800e8, validateAddress("FA3AEL2H9XZy3n199USs2poCEJBkK1Egy6JXhLehfLJjUYMKh1zS")}, + + // Centis BV total: 1800 FCT + HardGrant{221126, 1800e8, validateAddress("FA2hvRaci9Kks9cLNkEUFcxzUJuUFaaAE1eWYLqa2qk1k9pVFVBp")}, + // -------------------------------------------------------- + + // -------------------------------------------------------- + // Factom Inc. -- 900 FCT + // Oracle Master Dec 9 2019 - March 9, 2020 + + // Factom Inc. total: 900 FCT + HardGrant{221126, 900e8, validateAddress("FA3fpiZ91MCRRFjVGfNXK4pg7vx3BT3aSRyoVqgptZCX7N5BNR8P")}, + // -------------------------------------------------------- + + // -------------------------------------------------------- + // Factom Inc. -- 660 FCT + // Anchor Master Dec 9 2019 - March 9, 2020 + + // Factom Inc. total: 660 FCT + HardGrant{221126, 660e8, validateAddress("FA3jySUFtLXb1VdAJJ5NRVNYEtZ4EBSkDB7yn6LuKGQ4P1ntARhx")}, + // -------------------------------------------------------- + + // -------------------------------------------------------- + // David Chapman-Factomize -- 15769 FCT + // Core and General Development 2019-12-01 - 2020-02-29 + + // Factomize total: 15769 FCT + HardGrant{221126, 15769e8, validateAddress("FA3nsSjUy5uSkqMEug8t3VcehZn5w2ciSMpgqFEEsMRwMrHoa9k3")}, + // -------------------------------------------------------- + + // -------------------------------------------------------- + // CryptoLogic-The Factoid Authority-Crypto Logic-Bedrock Solutions-De Facto -- 1956 FCT + // Factom Open Node Continuity 2019-12-01 - 2020-02-29 + + // The Factoid Authority total: 326 FCT + HardGrant{221126, 326e8, validateAddress("FA2LV4s7LKA9BTgWaJNvcr9Yq8rpiH2XD3vEPY3nwSiNSrnRgkpK")}, + + // Crypto Logic total: 326 FCT + HardGrant{221126, 326e8, validateAddress("FA29wMUjN38BVLbJs6dR6gHHdBys2mpo3wy565JCjquUQTGqNZfb")}, + + // Bedrock Solutions total: 652 FCT + HardGrant{221126, 652e8, validateAddress("FA2FqYZPfBeRWq7fWSFEhassT5zpMQZm8jwus3yWbzeN3PZPWybm")}, + + // De Facto total: 652 FCT + HardGrant{221126, 652e8, validateAddress("FA2YeMbN8Z1SsT7Yqw6Np85kWwtFVg2CyJKMDFnuXTawWuWPtzvX")}, + // -------------------------------------------------------- + + // -------------------------------------------------------- + // Nolan Bauer-factomatic-David Kuiper-Factom Inc. -- 34740 FCT + // Protocol Development Dec 9 2019 - March 9, 2020 + + // Nolan Bauer total: 300 FCT + HardGrant{221126, 300e8, validateAddress("FA2oecgJW3XWnXzHhQQoULmMeKC97uAgHcPd4kEowTb3csVkbDc9")}, + + // factomatic total: 300 FCT + HardGrant{221126, 300e8, validateAddress("FA2944TXTDQKdJDp3TLSANjgMjwK2pQnTSkzE3kQcHWKetCCphcH")}, + + // David Kuiper total: 300 FCT + HardGrant{221126, 300e8, validateAddress("FA2FqYZPfBeRWq7fWSFEhassT5zpMQZm8jwus3yWbzeN3PZPWybm")}, + + // Factom Inc. total: 33840 FCT + HardGrant{221126, 33840e8, validateAddress("FA3LwCDE3ZdFkr9nE1Keb5JcHgwXVWpEHydshT1x2qKFdvZELVQz")}, + // -------------------------------------------------------- + + // -------------------------------------------------------- + // Samuel Vanderwaal-Exchange Committee -- 5000 FCT + // Legal Fees Reimbursement + + // Exchange Committee total: 5000 FCT + HardGrant{221126, 5000e8, validateAddress("FA2fVnbYw3Hr1MCJWktLnGDrYiLnW2HwDBDVRpp3Q355jJRDwvCs")}, + // -------------------------------------------------------- + + // -------------------------------------------------------- + // David Chapman-Factomize -- 12069 FCT + // ANO Promotion / Demotion System 2019-12-01 - 2020-02-29 + + // Factomize total: 12069 FCT + HardGrant{221126, 12069e8, validateAddress("FA3nsSjUy5uSkqMEug8t3VcehZn5w2ciSMpgqFEEsMRwMrHoa9k3")}, + // -------------------------------------------------------- + + // -------------------------------------------------------- + // mboender-Sphereon -- 14909 FCT + // Core Development Continuation 2019-12-02 - 2020-02-28 + + // Sphereon total: 14909 FCT + HardGrant{221126, 14909e8, validateAddress("FA3igxrULYqL5w4oq9vXwWzWURtvejfYh64iPZaeRqZfLpFdkFgD")}, + // -------------------------------------------------------- + + // -------------------------------------------------------- + // Samuel Vanderwaal-Exchange Committee -- 175 FCT + // G Suite Service + + // Exchange Committee total: 175 FCT + HardGrant{221126, 175e8, validateAddress("FA2kd7iAuCrR2GTV39UMaBTphzvQZYVYmvLJYGsjoJRjEGoVNQFd")}, + // -------------------------------------------------------- + + // -------------------------------------------------------- + // Devon Katz-Canonical Ledgers-DBGrow -- 5448 FCT + // FAT Development 4 - Continuation 2019-12-01 - 2020-02-01 + + // Canonical Ledgers total: 2980 FCT + HardGrant{221126, 2980e8, validateAddress("FA2xccSAfhGm5k4tPaXF9741xkQ52drWjoJodQhpPxDxepdqasMM")}, + + // DBGrow total: 2468 FCT + HardGrant{221126, 2468e8, validateAddress("FA3HSuFo9Soa5ZnG82JHqyKiRi4Pw17LxPTo9AsCaFNLCGkXkgsu")}, + // -------------------------------------------------------- + + // -------------------------------------------------------- + // David Chapman-Motion Factory-Factomize -- 800 FCT + // PegNet Explainer Video Backpay Grant + + // Motion Factory total: 800 FCT + HardGrant{221126, 800e8, validateAddress("FA3cRbg1CAzBvXpp8v3AfGuxzjePDbwqGr71MvLNanTX2QKDF1Cz")}, + // -------------------------------------------------------- + + // -------------------------------------------------------- + // Valentin Ganev-Factomatic -- 5500 FCT + // Python DID library + + // Factomatic total: 5500 FCT + HardGrant{221126, 5500e8, validateAddress("FA2QuNHNxgJBZyPggxkU8C16YReA4xFFxUvAGpu9azF3TaZg46SF")}, + // -------------------------------------------------------- + + // -------------------------------------------------------- + // Cody B -- 950 FCT + // Discord Daily Digest Screenshot (Continuation) 2019-11-01 - 2020-01-31 + + // Cody B total: 950 FCT + HardGrant{221126, 950e8, validateAddress("FA2WvyDhuKerk3RyVeSiTUonmcn7RuPRJGFQB6oCfroSys7NW3q2")}, + // -------------------------------------------------------- + + // -------------------------------------------------------- + // Jason Gregoire-Kompendium -- 730 FCT + // [Back-pay] Further Development of Open API – Client Library & Server Expansion + + // Kompendium total: 730 FCT + HardGrant{221126, 730e8, validateAddress("FA3KMPNX8AKdY3tjyKMzZ5cAkqUv97d3QqkJeQnVXk6PXSVgArnr")}, + // -------------------------------------------------------- + + // ******************************** + // ** BUG Bounty payout 2019-04 ** + // ******************************** + // -------------------------------------------------------- + // Niels Klomp -- Core committee -- 205 FCT + // This grant is for the Bug Bounty program and pays out 3 submissions, BB-001, BB-002 and BB-003 + // Payout will go to the following addresses, with a managed intermediary address. + // (Not all exchanges support coinbase transactions) + // + // BB-001 -- 17 FCT -- FA3ShvrkkVrCWGGiGqmAPykhLHhuZZtetUYBTYEss4KPsm5c7G6c + // BB-002 -- 17 FCT -- FA29JkXWYzwgLpQcrqSPsouy23AMgbrQcr6PTbAu6Q2tGXNveL4P + // BB-003 -- 171 FCT -- FA23fEJadueuxf8SsohpnDj3QYmZQ7mpMjWrFb1GHTQhByyDYMxe + // + // Full details for core committee: + // https://docs.google.com/spreadsheets/d/15fo9PCNt4meJGSd-V9iZENAiPTUx7dLpd990tH1dADY/edit#gid=0 + + // Bug bounty total: 205 FCT, remaining funds: 795 FCT + HardGrant{221126, 205e8, validateAddress("FA22J4Age2aLKRw1cKQckTPzK6Wpb6GCKKdYv6dp7SVsxzGyNqy4")}, + // -------------------------------------------------------- + } default: diff --git a/state/replay.go b/state/replay.go index 5fc4e913cf..b1336ff793 100644 --- a/state/replay.go +++ b/state/replay.go @@ -242,6 +242,9 @@ func (r *Replay) validate(mask int, hash [32]byte, timestamp interfaces.Timestam // Check the timestamp to see if within 12 hours of the system time. That not valid, we are // just done without any added concerns. if diff > Range || diff < -Range { + if mask == constants.INTERNAL_REPLAY { + return -1, true // if it's outside the replay bounds it is allowed. + } //fmt.Println("Time in hours, range:", hours(timeSeconds-systemTimeSeconds), HourRange) return -1, false } @@ -296,7 +299,7 @@ func (r *Replay) IsTSValidAndUpdateState(mask int, hash [32]byte, timestamp inte index, rval := r.validate(mask, hash, timestamp, systemtime) if rval { // Mark this hash as seen - if mask != constants.TIME_TEST { + if mask != constants.TIME_TEST && index != -1 { // INTERNAL_REPLAY that are outside the time window are ok r.Buckets[index][hash] = r.Buckets[index][hash] | mask r.Mutex.Unlock() //r.s.LogPrintf("replay", "Add %x (%s) to %s from %s", hash[:3], maskToString(mask), r.Name, atomic.WhereAmIString(1)) diff --git a/state/replay_test.go b/state/replay_test.go index ab7b3f6c57..718c780259 100644 --- a/state/replay_test.go +++ b/state/replay_test.go @@ -60,7 +60,7 @@ func Test_Replay(test *testing.T) { for in := 0; in < 1000; in++ { i := rand.Int() % len(mhs) x := mhs[i] - if r.IsTSValidAndUpdateState(constants.INTERNAL_REPLAY, x.hash, x.time, now) { + if r.IsTSValidAndUpdateState(constants.NETWORK_REPLAY, x.hash, x.time, now) { fmt.Printf("Failed Repeat Test %d\n", i) test.Fail() return @@ -83,7 +83,7 @@ func Test_Replay(test *testing.T) { ntime := now.GetTimeSeconds()/60 - spanMin + delta x.time = primitives.NewTimestampFromSeconds(uint32(ntime * 60)) - if _, ok := r.Valid(constants.INTERNAL_REPLAY, x.hash, x.time, now); !ok { + if _, ok := r.Valid(constants.NETWORK_REPLAY, x.hash, x.time, now); !ok { mnow := Minutes(now.GetTimeSeconds()) mx := Minutes(x.time.GetTimeSeconds()) fmt.Printf("Failed an element in the time range. Test %d element Time: %d now %d Diff %d\n", i, mx, mnow, mx-mnow) @@ -92,14 +92,14 @@ func Test_Replay(test *testing.T) { } // The first time we test, it should be valid. - if !r.IsTSValidAndUpdateState(constants.INTERNAL_REPLAY, x.hash, x.time, now) { + if !r.IsTSValidAndUpdateState(constants.NETWORK_REPLAY, x.hash, x.time, now) { fmt.Println("Failed Test ", i, "first") test.Fail() return } // An immediate replay! Should fail! - if r.IsTSValidAndUpdateState(constants.INTERNAL_REPLAY, x.hash, x.time, now) { + if r.IsTSValidAndUpdateState(constants.NETWORK_REPLAY, x.hash, x.time, now) { fmt.Println("Failed Test ", i, "second") test.Fail() return @@ -113,8 +113,8 @@ func Test_Replay(test *testing.T) { ntime = now.GetTimeSeconds()/60 - delta x.time = primitives.NewTimestampFromSeconds(uint32(ntime * 60)) - // should not be valid - if _, ok := r.Valid(constants.INTERNAL_REPLAY, x.hash, x.time, now); ok { + // should not be valid, INTERNAL_REPLAY is valid outside of the time window + if _, ok := r.Valid(constants.NETWORK_REPLAY, x.hash, x.time, now); ok { mnow := Minutes(now.GetTimeSeconds()) mx := Minutes(x.time.GetTimeSeconds()) fmt.Printf("Okayed an element out of time range. Test %d element Time: %d now %d Diff %d\n", i, mx, mnow, mx-mnow) @@ -123,7 +123,7 @@ func Test_Replay(test *testing.T) { } // The first time we test, it should not be valid. - if r.IsTSValidAndUpdateState(constants.INTERNAL_REPLAY, x.hash, x.time, now) { + if r.IsTSValidAndUpdateState(constants.NETWORK_REPLAY, x.hash, x.time, now) { fmt.Println("Failed Test ", i, "bad first") test.Fail() return diff --git a/state/safeMsgMap.go b/state/safeMsgMap.go index a33eea3fb7..b4c5888fba 100644 --- a/state/safeMsgMap.go +++ b/state/safeMsgMap.go @@ -6,7 +6,6 @@ import ( "github.com/FactomProject/factomd/common/constants" "github.com/FactomProject/factomd/common/interfaces" - "github.com/FactomProject/factomd/common/messages" "github.com/FactomProject/factomd/util/atomic" ) @@ -92,37 +91,18 @@ func (m *SafeMsgMap) Reset() { // Cleanup will clean old elements out from the commit map. func (m *SafeMsgMap) Cleanup(s *State) { m.Lock() - // Time out commits every now and again. Also check for entries that have been revealed - now := s.GetTimestamp() + // Time out commits every leaderTimestamp and again. Also check for entries that have been revealed + leaderTimestamp := s.GetLeaderTimestamp() for k, msg := range m.msgmap { - cc, ok := msg.(*messages.CommitChainMsg) - if ok && !s.NoEntryYet(cc.CommitChain.EntryHash, now) { - msg, ok := m.msgmap[k] - if ok { - defer m.s.LogMessage(m.name, "cleanup_chain", msg) - } - delete(m.msgmap, k) - continue - } - - c, ok := msg.(*messages.CommitEntryMsg) - if ok && !s.NoEntryYet(c.CommitEntry.EntryHash, now) { - msg, ok := m.msgmap[k] - if ok { - defer m.s.LogMessage(m.name, "cleanup_entry", msg) - } - delete(m.msgmap, k) - continue - } - - _, ok = s.Replay.Valid(constants.TIME_TEST, msg.GetRepeatHash().Fixed(), msg.GetTimestamp(), now) + _, ok := s.Replay.Valid(constants.TIME_TEST, msg.GetRepeatHash().Fixed(), msg.GetTimestamp(), leaderTimestamp) if !ok { msg, ok := m.msgmap[k] if ok { defer m.s.LogMessage(m.name, "cleanup_timeout", msg) } delete(m.msgmap, k) + continue } ok = s.Replay.IsHashUnique(constants.REVEAL_REPLAY, k) if !ok { @@ -131,6 +111,7 @@ func (m *SafeMsgMap) Cleanup(s *State) { defer m.s.LogMessage(m.name, "cleanup_replay", msg) } delete(m.msgmap, k) + continue } } m.Unlock() @@ -142,8 +123,9 @@ func (m *SafeMsgMap) RemoveExpired(s *State) { // Time out commits every now and again. for k, v := range m.msgmap { if v != nil { - _, ok := s.Replay.Valid(constants.TIME_TEST, v.GetRepeatHash().Fixed(), v.GetTimestamp(), s.GetTimestamp()) + _, ok := s.Replay.Valid(constants.TIME_TEST, v.GetRepeatHash().Fixed(), v.GetTimestamp(), s.GetLeaderTimestamp()) if !ok { + defer m.s.LogMessage(m.name, "RemoveExpired", v) delete(m.msgmap, k) } } diff --git a/state/state.go b/state/state.go index 61c9afa67b..1597553ae6 100644 --- a/state/state.go +++ b/state/state.go @@ -220,7 +220,7 @@ type State struct { IgnoreMissing bool // Timout and Limit for outstanding missing DBState requests - RequestTimeout time.Duration + RequestTimeout int // timeout in seconds RequestLimit int LLeaderHeight uint32 @@ -812,10 +812,7 @@ func (s *State) LoadConfig(filename string, networkFlag string) { s.ControlPanelPort = cfg.App.ControlPanelPort s.RpcUser = cfg.App.FactomdRpcUser s.RpcPass = cfg.App.FactomdRpcPass - // if RequestTimeout is not set by the configuration it will default to 0. - // If it is 0, the loop that uses it will set it to the blocktime/20 - // We set it there, as blktime might change after this function (from mainnet selection) - s.RequestTimeout = time.Duration(cfg.App.RequestTimeout) * time.Second + s.RequestTimeout = cfg.App.RequestTimeout s.RequestLimit = cfg.App.RequestLimit s.StateSaverStruct.FastBoot = cfg.App.FastBoot @@ -2333,34 +2330,39 @@ func (s *State) GetMessageFilterTimestamp() interfaces.Timestamp { // this ensure messages from prior boot and messages that predate the current replay filter are // are dropped. // It marks the start of the replay filter content -func (s *State) SetMessageFilterTimestamp(leaderTS interfaces.Timestamp) { +func (s *State) SetMessageFilterTimestamp(requestedTS interfaces.Timestamp) { + s.LogPrintf("executeMsg", "SetMessageFilterTimestamp(%s) from %s", requestedTS.String(), atomic.WhereAmIString(1)) - // make a copy of the time stamp so we don't change the source - requestedTS := new(primitives.Timestamp) - requestedTS.SetTimestamp(leaderTS) + // create a new timestamp + appliedTimestamp := primitives.NewTimestampFromMilliseconds(requestedTS.GetTimeMilliUInt64()) - onehourago := new(primitives.Timestamp) - onehourago.SetTimeMilli(primitives.NewTimestampNow().GetTimeMilli() - 60*60*1000) // now() - one hour + // It's ok for the follower to be working on a block that is two behind the network, the block two back could be + // 20 before now and it accepts commits from up to an hour before it plus we gave messages 10 minutes to transit + // gossip network so 20 + 60 + 10 = 90 minutes ago is ok. + historicallimit := new(primitives.Timestamp) + historicallimit.SetTimeMilli(primitives.NewTimestampNow().GetTimeMilli() - 90*60*1000) // now() - 90 minutes - if requestedTS.GetTimeMilli() < onehourago.GetTimeMilli() { - requestedTS.SetTimestamp(onehourago) + if appliedTimestamp.GetTimeMilli() < historicallimit.GetTimeMilli() { + // s.LogPrintf("executeMsg", "SetMessageFilterTimestamp(%s) -> %s moving to historical limit %s", requestedTS.String(), appliedTimestamp.String(), historicallimit.String()) + appliedTimestamp.SetTimestamp(historicallimit) } // build a timestamp 20 minutes before boot so we will accept messages from nodes who booted before us. preBootTime := new(primitives.Timestamp) - preBootTime.SetTimeMilli(s.TimestampAtBoot.GetTimeMilli() - 20*60*1000) + preBootTime.SetTimeMilli(s.TimestampAtBoot.GetTimeMilli() - constants.PreBootWindow*60*1000) - if requestedTS.GetTimeMilli() < preBootTime.GetTimeMilli() { - requestedTS.SetTimestamp(preBootTime) + if appliedTimestamp.GetTimeMilli() < preBootTime.GetTimeMilli() { + // s.LogPrintf("executeMsg", "SetMessageFilterTimestamp(%s) -> %s moving to preboot limit %s", requestedTS.String(), appliedTimestamp.String(), preBootTime.String()) + appliedTimestamp.SetTimestamp(preBootTime) } - if s.messageFilterTimestamp != nil && requestedTS.GetTimeMilli() < s.messageFilterTimestamp.GetTimeMilli() { - s.LogPrintf("executeMsg", "Set MessageFilterTimestamp attempt to move backward in time from %s", atomic.WhereAmIString(1)) - return + if s.messageFilterTimestamp != nil && appliedTimestamp.GetTimeMilli() < s.messageFilterTimestamp.GetTimeMilli() { + // s.LogPrintf("executeMsg", "SetMessageFilterTimestamp(%s) -> %s moving to current limit %s", requestedTS.String(), appliedTimestamp.String(), s.messageFilterTimestamp.String()) + appliedTimestamp.SetTimestamp(s.messageFilterTimestamp) } - s.LogPrintf("executeMsg", "SetMessageFilterTimestamp(%s) using %s ", leaderTS, requestedTS.String()) - s.messageFilterTimestamp = primitives.NewTimestampFromMilliseconds(requestedTS.GetTimeMilliUInt64()) + // s.LogPrintf("executeMsg", "SetMessageFilterTimestamp(%s) using %s ", requestedTS.String(), appliedTimestamp.String()) + s.messageFilterTimestamp = appliedTimestamp } func (s *State) GotHeartbeat(heartbeatTS interfaces.Timestamp, dbheight uint32) { @@ -2380,14 +2382,14 @@ func (s *State) GotHeartbeat(heartbeatTS interfaces.Timestamp, dbheight uint32) "%d)", heartbeatTS, dbheight) // set filter to one hour before target - s.SetMessageFilterTimestamp(primitives.NewTimestampFromMilliseconds(uint64(newTS - 60*60*1000))) + // s.SetMessageFilterTimestamp(primitives.NewTimestampFromMilliseconds(uint64(newTS - 60*60*1000))) // set Highest Ack & Known blocks s.SetHighestAck(dbheight) s.SetHighestKnownBlock(dbheight) // re-center replay filter - s.Replay.Recenter(primitives.NewTimestampFromMilliseconds(uint64(newTS))) + // s.Replay.Recenter(primitives.NewTimestampFromMilliseconds(uint64(newTS))) } func (s *State) SetLeaderTimestamp(ts interfaces.Timestamp) { diff --git a/state/stateConsensus.go b/state/stateConsensus.go index 66ae1c22b1..0ea150286c 100644 --- a/state/stateConsensus.go +++ b/state/stateConsensus.go @@ -93,14 +93,6 @@ func (s *State) DeleteFromHolding(hash [32]byte, msg interfaces.IMsg, reason str var FilterTimeLimit = int64(Range * 60 * 2 * 1000000000) // Filter hold two hours of messages, one in the past one in the future -func (s *State) GetFilterTimeNano() int64 { - t := s.GetMessageFilterTimestamp().GetTime().UnixNano() // this is the start of the filter - if t == 0 { - panic("got 0 time") - } - return t -} - // this is the common validation to all messages. they must not be a reply, they must not be out size the time window // for the replay filter. func (s *State) Validate(msg interfaces.IMsg) (validToSend int, validToExec int) { @@ -126,7 +118,7 @@ func (s *State) Validate(msg interfaces.IMsg) (validToSend int, validToExec int) if constants.NeedsAck(msg.Type()) { // Make sure we don't put in an old ack'd message (outside our repeat filter range) - filterTime := s.GetFilterTimeNano() + filterTime := s.GetMessageFilterTimestamp().GetTime().UnixNano() if filterTime == 0 { panic("got 0 time") } diff --git a/util/config.go b/util/config.go index f9bf98759c..f00efd90e3 100644 --- a/util/config.go +++ b/util/config.go @@ -67,9 +67,11 @@ type FactomdConfig struct { FactomdTlsPublicCert string FactomdRpcUser string FactomdRpcPass string - RequestTimeout int - RequestLimit int - CorsDomains string + // Timout and Limit for outstanding missing DBState requests + RequestTimeout int // timeout in seconds + RequestLimit int + + CorsDomains string ChangeAcksHeight uint32 } @@ -174,9 +176,8 @@ FactomdRpcPass = "" ; RequestTimeout is the amount of time in seconds before a pending request for a ; missing DBState is considered too old and the state is put back into the -; missing states list. If RequestTimout is not set or is set to 0 it will become -; 1/10th of DirectoryBlockInSeconds -;RequestTimeout = 30 +; missing states list. +RequestTimeout = 30 ; RequestLimit is the maximum number of pending requests for missing states. ; factomd will stop making DBStateMissing requests until current requests are ; moved out of the waiting list diff --git a/wsapi/server.go b/wsapi/server.go index 377be904b5..4450e5c0d4 100644 --- a/wsapi/server.go +++ b/wsapi/server.go @@ -5,6 +5,7 @@ import ( "crypto/tls" "fmt" "net/http" + "strconv" "time" "github.com/FactomProject/factomd/common/interfaces" @@ -19,6 +20,7 @@ type Server struct { tlsEnabled bool certFile string keyFile string + Port string } type Middleware func(http.HandlerFunc) http.HandlerFunc @@ -28,7 +30,8 @@ func InitServer(state interfaces.IState) *Server { address := fmt.Sprintf(":%d", state.GetPort()) router := mux.NewRouter() - server := Server{State: state, router: router, tlsEnabled: tlsIsEnabled, certFile: certFile, keyFile: keyFile} + port := strconv.Itoa(state.GetPort()) + server := Server{State: state, router: router, tlsEnabled: tlsIsEnabled, certFile: certFile, keyFile: keyFile, Port: port} if tlsIsEnabled { router.Schemes("HTTPS") @@ -95,9 +98,20 @@ func APILogger() Middleware { } } +// IDInjector injects the server's ID into every request as http header +func IDInjector(server *Server) Middleware { + return func(f http.HandlerFunc) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + r.Header.Set("factomd-port", server.Port) + f(w, r) + } + } +} + // add route and Chain applies middlewares to a http.HandlerFunc func (server *Server) addRoute(path string, f func(http.ResponseWriter, *http.Request), middlewares ...Middleware) *mux.Route { middlewares = append(middlewares, APILogger()) + middlewares = append(middlewares, IDInjector(server)) for _, m := range middlewares { f = m(f) } diff --git a/wsapi/wsapi.go b/wsapi/wsapi.go index c10bc9682c..0856fc0a8f 100644 --- a/wsapi/wsapi.go +++ b/wsapi/wsapi.go @@ -12,7 +12,6 @@ import ( "errors" "fmt" "io/ioutil" - "net" "net/http" "os" "strconv" @@ -93,15 +92,10 @@ func SetState(state interfaces.IState) { } func GetState(r *http.Request) (state interfaces.IState, err error) { - _, port, err := net.SplitHostPort(r.Host) - if err != nil { - return nil, errors.New(fmt.Sprintf("failed to extract port from request: %s", err)) - } - + ServersMutex.Lock() + defer ServersMutex.Unlock() + port := r.Header.Get("factomd-port") if server, ok := Servers[port]; ok { - ServersMutex.Lock() - defer ServersMutex.Unlock() - return server.State, nil } else { return nil, errors.New(fmt.Sprintf("failed to get state, server initialization on port: %s failed", port))