private Snapshot Apply(Snapshot original, List <BlockHeader> headers, ulong epoch) { // Allow passing in no headers for cleaner code if (headers.Count == 0) { return(original); } // Sanity check that the headers can be applied for (int i = 0; i < headers.Count - 1; i++) { if (headers[i].Number != original.Number + i + 1) { throw new InvalidOperationException("Invalid voting chain"); } } // Iterate through the headers and create a new snapshot Snapshot snapshot = (Snapshot)original.Clone(); foreach (BlockHeader header in headers) { // Remove any votes on checkpoint blocks long number = header.Number; if ((ulong)number % epoch == 0) { snapshot.Votes.Clear(); snapshot.Tally.Clear(); } // Resolve the authorization key and check against signers Address signer = header.Author; if (!snapshot.Signers.ContainsKey(signer)) { throw new InvalidOperationException("Unauthorized signer"); } if (HasSignedRecently(snapshot, number, signer)) { throw new InvalidOperationException($"Recently signed (trying to sign {number} when last signed {snapshot.Signers[signer]} with {snapshot.Signers.Count} signers)"); } snapshot.Signers[signer] = number; // Header authorized, discard any previous votes for the signer for (int i = 0; i < snapshot.Votes.Count; i++) { Vote vote = snapshot.Votes[i]; if (vote.Signer == signer && vote.Address == header.Beneficiary) { // Uncast the vote from the cached tally Uncast(snapshot, vote.Address, vote.Authorize); // Uncast the vote from the chronological list snapshot.Votes.RemoveAt(i); break; } } // Tally up the new vote from the signer bool authorize = header.Nonce == Clique.NonceAuthVote; if (Cast(snapshot, header.Beneficiary, authorize)) { Vote vote = new Vote(signer, number, header.Beneficiary, authorize); snapshot.Votes.Add(vote); } // If the vote passed, update the list of signers Tally tally = snapshot.Tally[header.Beneficiary]; if (tally.Votes > snapshot.Signers.Count / 2) { if (tally.Authorize) { snapshot.Signers.Add(header.Beneficiary, 0); } else { snapshot.Signers.Remove(header.Beneficiary); } // Discard any previous votes the deauthorized signer cast for (int i = 0; i < snapshot.Votes.Count; i++) { if (snapshot.Votes[i].Signer == header.Beneficiary) { // Uncast the vote from the cached tally if (Uncast(snapshot, snapshot.Votes[i].Address, snapshot.Votes[i].Authorize)) { // Uncast the vote from the chronological list snapshot.Votes.RemoveAt(i); i--; } } } // Discard any previous votes around the just changed account for (int i = 0; i < snapshot.Votes.Count; i++) { if (snapshot.Votes[i].Address == header.Beneficiary) { snapshot.Votes.RemoveAt(i); i--; } } snapshot.Tally.Remove(header.Beneficiary); } } snapshot.Number += headers.Count; // was this needed? // snapshot.Hash = BlockHeader.CalculateHash(headers[headers.Count - 1]); snapshot.Hash = headers[headers.Count - 1].Hash; return(snapshot); }
public Snapshot Apply(List <BlockHeader> headers) { // Allow passing in no headers for cleaner code if (headers.Count == 0) { return(this); } // Sanity check that the headers can be applied for (int i = 0; i < headers.Count - 1; i++) { if (headers[i].Number != Number + (UInt256)i + 1) { throw new InvalidOperationException("Invalid voting chain"); } } // Iterate through the headers and create a new snapshot Snapshot snapshot = Clone(); foreach (BlockHeader header in headers) { // Remove any votes on checkpoint blocks UInt256 number = header.Number; if ((ulong)number % Config.Epoch == 0) { snapshot.Votes.Clear(); snapshot.Tally = new Dictionary <Address, Tally>(); } // Resolve the authorization key and check against signers Address signer = header.Author; if (!snapshot.Signers.ContainsKey(signer)) { throw new InvalidOperationException("Unauthorized signer"); } if (HasSignedRecently(snapshot, number, signer)) { throw new InvalidOperationException("Recently signed"); } snapshot.Signers[signer] = number; // Header authorized, discard any previous votes from the signer for (int i = 0; i < snapshot.Votes.Count; i++) { Vote vote = snapshot.Votes[i]; if (vote.Signer == signer && vote.Address == header.Beneficiary) { // Uncast the vote from the cached tally snapshot.Uncast(vote.Address, vote.Authorize); // Uncast the vote from the chronological list snapshot.Votes.RemoveAt(i); break; } } // Tally up the new vote from the signer bool authorize = header.Nonce == Clique.NonceAuthVote; if (snapshot.Cast(header.Beneficiary, authorize)) { Vote vote = new Vote(signer, number, header.Beneficiary, authorize); snapshot.Votes.Add(vote); } // If the vote passed, update the list of signers Tally tally = snapshot.Tally[header.Beneficiary]; if (tally.Votes > snapshot.Signers.Count / 2) { if (tally.Authorize) { snapshot.Signers.Add(header.Beneficiary, 0); } else { snapshot.Signers.Remove(header.Beneficiary); } // Discard any previous votes the deauthorized signer cast for (int i = 0; i < snapshot.Votes.Count; i++) { if (snapshot.Votes[i].Signer == header.Beneficiary) { // Uncast the vote from the cached tally snapshot.Uncast(snapshot.Votes[i].Address, snapshot.Votes[i].Authorize); // Uncast the vote from the chronological list snapshot.Votes.RemoveAt(i); i--; } } // Discard any previous votes around the just changed account for (int i = 0; i < snapshot.Votes.Count; i++) { if (snapshot.Votes[i].Address == header.Beneficiary) { snapshot.Votes.RemoveAt(i); i--; } } snapshot.Tally.Remove(header.Beneficiary); } } snapshot.Number += (ulong)headers.Count; snapshot.Hash = BlockHeader.CalculateHash(headers[headers.Count - 1]); return(snapshot); }