github.com/halybang/go-ethereum@v1.0.5-0.20180325041310-3b262bc1367c/consensus/ethash/snapshot.go (about)

     1  // Copyright 2018 Wanchain Foundation Ltd
     2  // Copyright 2017 The go-ethereum Authors
     3  // This file is part of the go-ethereum library.
     4  //
     5  // The go-ethereum library is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU Lesser General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  //
    10  // The go-ethereum library is distributed in the hope that it will be useful,
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    13  // GNU Lesser General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU Lesser General Public License
    16  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    17  // The code is mostly inspired by POA of ethereum
    18  
    19  package ethash
    20  
    21  import (
    22  	"container/list"
    23  	"encoding/json"
    24  	"errors"
    25  
    26  	"github.com/wanchain/go-wanchain/common"
    27  	"github.com/wanchain/go-wanchain/core/types"
    28  	"github.com/wanchain/go-wanchain/ethdb"
    29  	"strings"
    30  	"fmt"
    31  )
    32  
    33  const (
    34  	windowRatio = 2 //
    35  )
    36  
    37  type Snapshot struct {
    38  	PermissionSigners map[common.Address]struct{}
    39  
    40  	Number              uint64
    41  	Hash                common.Hash
    42  	UsedSigners         map[common.Address]struct{}
    43  	RecentSignersWindow *list.List
    44  }
    45  
    46  type plainSnapShot struct {
    47  	PermissionSigners map[common.Address]struct{} `json:"permissionSigners"`
    48  
    49  	Number              uint64                      `json:"number"`
    50  	Hash                common.Hash                 `json:"hash"`
    51  	UsedSigners         map[common.Address]struct{} `json:"usedSigners"`
    52  	RecentSignersWindow []common.Address            `json:"recentSignersWindow"`
    53  }
    54  
    55  func newSnapshot(number uint64, hash common.Hash, signers []common.Address) *Snapshot {
    56  	snap := &Snapshot{
    57  		PermissionSigners:   make(map[common.Address]struct{}),
    58  		Number:              number,
    59  		Hash:                hash,
    60  		UsedSigners:         make(map[common.Address]struct{}),
    61  		RecentSignersWindow: list.New(),
    62  	}
    63  
    64  	for _, s := range signers {
    65  		snap.PermissionSigners[s] = struct{}{}
    66  	}
    67  
    68  	return snap
    69  }
    70  
    71  func loadSnapShot(db ethdb.Database, hash common.Hash) (*Snapshot, error) {
    72  	blob, err := db.Get(append([]byte("ppow-"), hash[:]...))
    73  	if err != nil {
    74  		return nil, err
    75  	}
    76  
    77  	plain := new(plainSnapShot)
    78  	if err := json.Unmarshal(blob, plain); err != nil {
    79  		return nil, err
    80  	}
    81  
    82  	snap := &Snapshot{
    83  		PermissionSigners:   plain.PermissionSigners,
    84  		Number:              plain.Number,
    85  		Hash:                plain.Hash,
    86  		UsedSigners:         plain.UsedSigners,
    87  		RecentSignersWindow: list.New(),
    88  	}
    89  
    90  	for _, v := range plain.RecentSignersWindow {
    91  		snap.RecentSignersWindow.PushBack(v)
    92  	}
    93  
    94  	return snap, nil
    95  }
    96  
    97  func (s *Snapshot) store(db ethdb.Database) error {
    98  	plain := &plainSnapShot{
    99  		PermissionSigners:   s.PermissionSigners,
   100  		Number:              s.Number,
   101  		Hash:                s.Hash,
   102  		UsedSigners:         s.UsedSigners,
   103  		RecentSignersWindow: make([]common.Address, 0),
   104  	}
   105  
   106  	for e := s.RecentSignersWindow.Front(); e != nil; e = e.Next() {
   107  		if _, ok := e.Value.(common.Address); ok {
   108  			plain.RecentSignersWindow = append(plain.RecentSignersWindow, e.Value.(common.Address))
   109  		}
   110  	}
   111  
   112  	blob, err := json.Marshal(plain)
   113  	if err != nil {
   114  		return err
   115  	}
   116  
   117  	return db.Put(append([]byte("ppow-"), plain.Hash[:]...), blob)
   118  }
   119  
   120  func (s *Snapshot) copy() *Snapshot {
   121  	cpy := &Snapshot{
   122  		PermissionSigners:   s.PermissionSigners,
   123  		Number:              s.Number,
   124  		Hash:                s.Hash,
   125  		UsedSigners:         make(map[common.Address]struct{}),
   126  		RecentSignersWindow: list.New(),
   127  	}
   128  
   129  	for signer := range s.UsedSigners {
   130  		cpy.UsedSigners[signer] = struct{}{}
   131  	}
   132  	cpy.RecentSignersWindow.PushBackList(s.RecentSignersWindow)
   133  	return cpy
   134  }
   135  
   136  func (s *Snapshot) updateSignerStatus(signer common.Address, isExist bool) {
   137  	if isExist {
   138  		//if signer already presence
   139  		if (s.RecentSignersWindow.Len()) > 0 {
   140  			s.RecentSignersWindow.PushFront(signer)
   141  			s.RecentSignersWindow.Remove(s.RecentSignersWindow.Back())
   142  		}
   143  	} else {
   144  		// This is the first time the signer appear
   145  		s.UsedSigners[signer] = struct{}{}
   146  		preWindowLen := s.RecentSignersWindow.Len()
   147  		newWindowLen := (len(s.UsedSigners) - 1) / windowRatio
   148  		if newWindowLen > preWindowLen {
   149  			s.RecentSignersWindow.PushFront(signer)
   150  		} else {
   151  			//windowLen unchanged
   152  			if newWindowLen > 0 {
   153  				s.RecentSignersWindow.PushFront(signer)
   154  				s.RecentSignersWindow.Remove(s.RecentSignersWindow.Back())
   155  			}
   156  		}
   157  	}
   158  }
   159  
   160  // apply creates a new authorization snapshot by applying the given headers to
   161  // the original one.
   162  // PermissionSigners is the full set of signers who can sign blocks
   163  // UsedSigners is the set of signers who had sign blocks
   164  // RecentSignersWindow is the set who can not sign next block
   165  // len(RecentSignersWindow) = (len(UsedSigners)-1)/2
   166  // so when n > 2, hacker should got (n / 2 + 1) key to reorg chain?
   167  func (s *Snapshot) apply(headers []*types.Header) (*Snapshot, error) {
   168  	if len(headers) == 0 {
   169  		return s, nil
   170  	}
   171  	// Sanity check that the headers can be applied
   172  	for i := 0; i < len(headers)-1; i++ {
   173  		if headers[i+1].Number.Uint64() != headers[i].Number.Uint64()+1 {
   174  			return nil, errors.New("invalid applied headers")
   175  		}
   176  	}
   177  	if headers[0].Number.Uint64() != s.Number+1 {
   178  		return nil, errors.New("invalid applied header to snapshot")
   179  	}
   180  
   181  	snap := s.copy()
   182  
   183  	for _, header := range headers {
   184  		signer, err := ecrecover(header)
   185  		if err != nil || 0 != strings.Compare(signer.String(), header.Coinbase.String()) {
   186  			return nil, err
   187  		}
   188  
   189  		err = snap.isLegal4Sign(signer)
   190  		if err != nil {
   191  			return nil, err
   192  		}
   193  
   194  		_, ok := snap.UsedSigners[signer]
   195  		snap.updateSignerStatus(signer, ok)
   196  	}
   197  
   198  	snap.Hash = headers[len(headers)-1].Hash()
   199  	snap.Number = headers[len(headers)-1].Number.Uint64()
   200  
   201  	return snap, nil
   202  }
   203  
   204  func (s *Snapshot) isLegal4Sign(signer common.Address) error {
   205  	if _, ok := s.PermissionSigners[signer]; !ok {
   206  		fmt.Println(common.ToHex(signer[:]))
   207  		return errUnauthorized
   208  	}
   209  
   210  	for e := s.RecentSignersWindow.Front(); e != nil; e = e.Next() {
   211  		if _, ok := e.Value.(common.Address); ok {
   212  			wSigner := e.Value.(common.Address)
   213  			if signer == wSigner {
   214  				return errAuthorTooOften
   215  			}
   216  		}
   217  	}
   218  	return nil
   219  }