github.com/amazechain/amc@v0.1.3/internal/vm/absint_cfg_proof_gen.go (about)

     1  package vm
     2  
     3  import (
     4  	"bufio"
     5  	"encoding/hex"
     6  	"errors"
     7  	"fmt"
     8  	"os"
     9  	"sort"
    10  	"strconv"
    11  	"strings"
    12  	"time"
    13  
    14  	"github.com/emicklei/dot"
    15  	"github.com/holiman/uint256"
    16  )
    17  
    18  // ////////////////////////////////////////////////
    19  type AbsValueKind int
    20  
    21  //////////////////////////
    22  
    23  // stmt is the representation of an executable instruction - extension of an opcode
    24  type Astmt struct {
    25  	inferredAsData bool
    26  	ends           bool
    27  	isBlockEntry   bool
    28  	isBlockExit    bool
    29  	covered        bool
    30  	opcode         OpCode
    31  	operation      *operation
    32  	pc             int
    33  	numBytes       int
    34  	value          uint256.Int
    35  }
    36  
    37  func (stmt *Astmt) String() string {
    38  	ends := ""
    39  	if stmt.ends {
    40  		ends = "ends"
    41  	}
    42  	return fmt.Sprintf("%v %v %v", stmt.opcode, ends, stmt.operation.isPush)
    43  }
    44  
    45  type Program struct {
    46  	Code        []byte
    47  	Stmts       []*Astmt
    48  	Blocks      []*Block
    49  	Entry2block map[int]*Block
    50  	Exit2block  map[int]*Block
    51  }
    52  
    53  func (program *Program) GetCodeHex() string {
    54  	return hex.EncodeToString(program.Code)
    55  }
    56  
    57  func (program *Program) isJumpDest(value *uint256.Int) bool {
    58  	if !value.IsUint64() {
    59  		return false
    60  	}
    61  
    62  	pc := value.Uint64()
    63  	if pc >= uint64(len(program.Stmts)) {
    64  		return false
    65  	}
    66  
    67  	stmt := program.Stmts[pc]
    68  	if stmt == nil {
    69  		return false
    70  	}
    71  
    72  	return stmt.opcode == JUMPDEST
    73  }
    74  
    75  func toProgram(code []byte) *Program {
    76  	jt := newIstanbulInstructionSet()
    77  
    78  	program := &Program{Code: code}
    79  
    80  	codeLen := len(code)
    81  	inferIsData := make(map[int]bool)
    82  	for pc := 0; pc < codeLen; pc++ {
    83  		stmt := Astmt{}
    84  		stmt.pc = pc
    85  		stmt.inferredAsData = inferIsData[pc]
    86  
    87  		op := OpCode(code[pc])
    88  		stmt.opcode = op
    89  		stmt.operation = jt[op]
    90  		stmt.ends = stmt.operation == nil
    91  		//fmt.Printf("%v %v %v", pc, stmt.opcode, stmt.operation.valid)
    92  
    93  		if op.IsPush() {
    94  			pushByteSize := stmt.operation.opNum
    95  			startMin := pc + 1
    96  			if startMin >= codeLen {
    97  				startMin = codeLen
    98  			}
    99  			endMin := startMin + pushByteSize
   100  			if startMin+pushByteSize >= codeLen {
   101  				endMin = codeLen
   102  			}
   103  			integer := new(uint256.Int)
   104  			integer.SetBytes(code[startMin:endMin])
   105  			stmt.value = *integer
   106  			stmt.numBytes = pushByteSize + 1
   107  
   108  			if !stmt.inferredAsData {
   109  				for datapc := startMin; datapc < endMin; datapc++ {
   110  					inferIsData[datapc] = true
   111  				}
   112  			}
   113  		} else {
   114  			stmt.numBytes = 1
   115  		}
   116  
   117  		program.Stmts = append(program.Stmts, &stmt)
   118  		if pc != len(program.Stmts)-1 {
   119  			panic("Invalid length")
   120  		}
   121  	}
   122  
   123  	for pc, stmt := range program.Stmts {
   124  		if !stmt.inferredAsData {
   125  			if pc == 0 {
   126  				stmt.isBlockEntry = true
   127  			} else if stmt.opcode == JUMPDEST {
   128  				stmt.isBlockEntry = true
   129  			} else if stmt.opcode == JUMPI && pc < len(program.Stmts)-1 {
   130  				entry := program.Stmts[pc+1]
   131  				entry.isBlockEntry = true
   132  			}
   133  
   134  			if pc == len(program.Stmts)-1 {
   135  				stmt.isBlockExit = true
   136  			} else if stmt.opcode == JUMP || stmt.opcode == JUMPI {
   137  				stmt.isBlockExit = true
   138  			}
   139  		}
   140  	}
   141  
   142  	program.Entry2block = make(map[int]*Block)
   143  	program.Exit2block = make(map[int]*Block)
   144  
   145  	for entrypc, entry := range program.Stmts {
   146  		if entry.isBlockEntry {
   147  			block := &Block{Entrypc: entrypc, Stmts: make([]*Astmt, 0)}
   148  			program.Blocks = append(program.Blocks, block)
   149  			for i := entrypc; i < len(program.Stmts); i++ {
   150  				block.Stmts = append(block.Stmts, program.Stmts[i])
   151  				if program.Stmts[i].isBlockExit {
   152  					break
   153  				}
   154  			}
   155  
   156  			if len(block.Stmts) > 0 {
   157  				block.Exitpc = block.Stmts[len(block.Stmts)-1].pc
   158  			}
   159  
   160  			program.Entry2block[block.Entrypc] = block
   161  			program.Exit2block[block.Exitpc] = block
   162  		}
   163  	}
   164  
   165  	/*
   166  		epilogue := []byte{0xa1, 0x65, 0x62, 0x7a, 0x7a, 0x72}
   167  
   168  		for i := 0; i < len(program.Stmts) - len(epilogue) + 1; i++ {
   169  			match := true
   170  			for e := 0; e < len(epilogue); e++ {
   171  				if byte(program.Stmts[i+e].opcode) != epilogue[e] {
   172  					match = false
   173  					break
   174  				}
   175  			}
   176  			if match {
   177  				for j := i; j < len(program.Stmts); j++ {
   178  					program.Stmts[j].inferredAsData = true
   179  					program.Stmts[j].epilogue = true
   180  				}
   181  				break
   182  			}
   183  		}*/
   184  
   185  	return program
   186  }
   187  
   188  /*
   189  func printStmts(stmts []*Astmt) {
   190  	for i, stmt := range stmts {
   191  		fmt.Printf("%v %v\n", i, stmt)
   192  	}
   193  }*/
   194  
   195  ////////////////////////
   196  
   197  type edge struct {
   198  	pc0    int
   199  	stmt   *Astmt
   200  	pc1    int
   201  	isJump bool
   202  }
   203  
   204  func (e edge) String() string {
   205  	return fmt.Sprintf("%v %v %v", e.pc0, e.pc1, e.stmt.opcode)
   206  }
   207  
   208  // resolve analyses given executable instruction at given program counter in the context of given state
   209  // It either concludes that the execution of the instruction may result in a jump to an unpredictable
   210  // destination (in this case, attrubute resolved will be false), or returns one (for a non-JUMPI) or two (for JUMPI)
   211  // edges that contain program counters of destinations where the executions can possibly come next
   212  func resolve(cfg *Cfg, pc0 int) ([]edge, error) {
   213  	st0 := cfg.D[pc0]
   214  
   215  	stmt := cfg.Program.Stmts[pc0]
   216  
   217  	if stmt.ends {
   218  		return nil, nil
   219  	}
   220  
   221  	codeLen := len(cfg.Program.Code)
   222  
   223  	var edges []edge
   224  	isBadJump := false
   225  
   226  	//jump edges
   227  	for _, stack := range st0.stackset {
   228  		if stmt.opcode == JUMP || stmt.opcode == JUMPI {
   229  			if stack.hasIndices(0) {
   230  				jumpDest := stack.values[0]
   231  				if jumpDest.kind == InvalidValue {
   232  					//program terminates, don't add edges
   233  				} else if jumpDest.kind == TopValue {
   234  					isBadJump = true
   235  					cfg.Metrics.BadJumpImprecision = true
   236  				} else if jumpDest.kind == ConcreteValue {
   237  					if cfg.Program.isJumpDest(jumpDest.value) {
   238  						pc1 := int(jumpDest.value.Uint64())
   239  						edges = append(edges, edge{pc0, stmt, pc1, true})
   240  					}
   241  				}
   242  			}
   243  		}
   244  	}
   245  
   246  	//fall-thru edge
   247  	if stmt.opcode != JUMP {
   248  		if pc0 < codeLen-stmt.numBytes {
   249  			edges = append(edges, edge{pc0, stmt, pc0 + stmt.numBytes, false})
   250  		}
   251  	}
   252  
   253  	for _, e := range edges {
   254  		if cfg.PrevEdgeMap[e.pc1] == nil {
   255  			cfg.PrevEdgeMap[e.pc1] = make(map[int]bool)
   256  		}
   257  		cfg.PrevEdgeMap[e.pc1][e.pc0] = true
   258  
   259  		cfg.Program.Stmts[e.pc0].covered = true
   260  		cfg.Program.Stmts[e.pc1].covered = true
   261  	}
   262  
   263  	if isBadJump {
   264  		cfg.BadJumps[stmt.pc] = true
   265  		cfg.Metrics.Unresolved = true
   266  		cfg.checkRep()
   267  		return nil, errors.New("unresolvable jumps found")
   268  	}
   269  
   270  	edges = sortAndUnique(edges)
   271  
   272  	cfg.checkRep()
   273  	return edges, nil
   274  }
   275  
   276  type edgeKey struct {
   277  	pc0 int
   278  	pc1 int
   279  }
   280  
   281  func sortAndUnique(edges []edge) []edge {
   282  	uedges := make([]edge, 0)
   283  
   284  	edgeMap := make(map[edgeKey]bool)
   285  	for i := 0; i < len(edges); i++ {
   286  		e := edges[i]
   287  		ek := edgeKey{e.pc0, e.pc1}
   288  		if edgeMap[ek] {
   289  			continue
   290  		}
   291  
   292  		edgeMap[ek] = true
   293  		uedges = append(uedges, e)
   294  	}
   295  
   296  	sort.SliceStable(uedges, func(i, j int) bool {
   297  		return uedges[i].pc0 < uedges[j].pc1
   298  	})
   299  
   300  	return uedges
   301  }
   302  
   303  func post(cfg *Cfg, st0 *astate, edge edge, maxStackLen int) (*astate, error) {
   304  	st1 := emptyState()
   305  	stmt := edge.stmt
   306  
   307  	for _, stack0 := range st0.stackset {
   308  		if edge.isJump {
   309  			if !stack0.hasIndices(0) {
   310  				continue
   311  			}
   312  
   313  			elm0 := stack0.values[0]
   314  			if elm0.kind == ConcreteValue && elm0.value.IsUint64() && int(elm0.value.Uint64()) != edge.pc1 {
   315  				continue
   316  			}
   317  		}
   318  
   319  		stack1 := stack0.Copy()
   320  
   321  		if stmt.opcode.IsPush() {
   322  			if cfg.Program.isJumpDest(&stmt.value) || isFF(&stmt.value) {
   323  				stack1.Push(AbsValueConcrete(stmt.value))
   324  			} else {
   325  				stack1.Push(AbsValueInvalid())
   326  			}
   327  		} else if stmt.operation.isDup {
   328  			if !stack0.hasIndices(stmt.operation.opNum - 1) {
   329  				continue
   330  			}
   331  
   332  			value := stack1.values[stmt.operation.opNum-1]
   333  			stack1.Push(value)
   334  		} else if stmt.operation.isSwap {
   335  			opNum := stmt.operation.opNum
   336  
   337  			if !stack0.hasIndices(0, opNum) {
   338  				continue
   339  			}
   340  
   341  			a := stack1.values[0]
   342  			b := stack1.values[opNum]
   343  			stack1.values[0] = b
   344  			stack1.values[opNum] = a
   345  
   346  		} else if stmt.opcode == AND {
   347  			if !stack0.hasIndices(0, 1) {
   348  				continue
   349  			}
   350  
   351  			a := stack1.Pop(edge.pc0)
   352  			b := stack1.Pop(edge.pc0)
   353  
   354  			if a.kind == ConcreteValue && b.kind == ConcreteValue {
   355  				v := uint256.NewInt(0)
   356  				v.And(a.value, b.value)
   357  				stack1.Push(AbsValueConcrete(*v))
   358  			} else {
   359  				stack1.Push(AbsValueTop(edge.pc0))
   360  			}
   361  		} else if stmt.opcode == PC {
   362  			v := uint256.NewInt(0)
   363  			v.SetUint64(uint64(stmt.pc))
   364  			stack1.Push(AbsValueConcrete(*v))
   365  		} else {
   366  			if !stack0.hasIndices(stmt.operation.numPop - 1) {
   367  				continue
   368  			}
   369  
   370  			for i := 0; i < stmt.operation.numPop; i++ {
   371  				stack1.Pop(edge.pc0)
   372  			}
   373  
   374  			for i := 0; i < stmt.operation.numPush; i++ {
   375  				stack1.Push(AbsValueTop(edge.pc0))
   376  			}
   377  		}
   378  
   379  		stack1.updateHash()
   380  		st1.Add(stack1)
   381  	}
   382  
   383  	for _, stack := range st1.stackset {
   384  		if len(stack.values) > maxStackLen {
   385  			cfg.Metrics.ShortStack = true
   386  			return nil, errors.New("max stack length reached")
   387  		}
   388  	}
   389  
   390  	return st1, nil
   391  }
   392  
   393  func isFF(u *uint256.Int) bool {
   394  	if u.IsUint64() && u.Uint64() == 4294967295 {
   395  		return true
   396  	}
   397  	return false
   398  }
   399  
   400  type Block struct {
   401  	Entrypc int
   402  	Exitpc  int
   403  	Stmts   []*Astmt
   404  }
   405  
   406  type CfgMetrics struct {
   407  	Valid                  bool
   408  	Panic                  bool
   409  	Unresolved             bool
   410  	ShortStack             bool
   411  	AnlyCounterLimit       bool
   412  	LowCoverage            bool
   413  	BadJumpImprecision     bool
   414  	BadJumpInvalidOp       bool
   415  	BadJumpInvalidJumpDest bool
   416  	StackCountLimitReached bool
   417  	OOM                    bool
   418  	Timeout                bool
   419  	Checker                bool
   420  	CheckerFailed          bool
   421  	AnlyCounter            int
   422  	NumStacks              int
   423  	ProofSizeBytes         int
   424  	Time                   time.Duration
   425  	MemUsedMBs             uint64
   426  }
   427  
   428  type Cfg struct {
   429  	Program         *Program
   430  	BadJumps        map[int]bool
   431  	PrevEdgeMap     map[int]map[int]bool
   432  	D               map[int]*astate
   433  	Metrics         *CfgMetrics
   434  	ProofSerialized []byte
   435  }
   436  
   437  type CfgCoverageStats struct {
   438  	Covered      int
   439  	Uncovered    int
   440  	Instructions int
   441  	Coverage     int
   442  	Epilogue     int
   443  }
   444  
   445  func (cfg *Cfg) Clear() {
   446  	cfg.D = nil
   447  	cfg.PrevEdgeMap = nil
   448  	cfg.BadJumps = nil
   449  	cfg.Program = nil
   450  }
   451  
   452  func (cfg *Cfg) checkRep() {
   453  	if true {
   454  		return
   455  	}
   456  
   457  	for pc1, pc0s := range cfg.PrevEdgeMap {
   458  		for pc0 := range pc0s {
   459  			s := cfg.Program.Stmts[pc0]
   460  			if s.ends {
   461  				msg := fmt.Sprintf("Halt has successor: %v -> %v", pc0, pc1)
   462  				panic(msg)
   463  			}
   464  		}
   465  	}
   466  }
   467  
   468  func (cfg *Cfg) GetCoverageStats() CfgCoverageStats {
   469  	stats := CfgCoverageStats{}
   470  	firstUncovered := -1
   471  	for pc, stmt := range cfg.Program.Stmts {
   472  		if !stmt.inferredAsData {
   473  			if stmt.covered {
   474  				stats.Covered++
   475  			} else {
   476  				if firstUncovered < 0 {
   477  					firstUncovered = pc
   478  				}
   479  			}
   480  			stats.Instructions++
   481  		}
   482  	}
   483  
   484  	stats.Epilogue = 0
   485  	for i := len(cfg.Program.Stmts) - 1; i >= 0; i-- {
   486  		stmt := cfg.Program.Stmts[i]
   487  		if !stmt.inferredAsData {
   488  			if cfg.Program.Stmts[i].covered {
   489  				break
   490  			}
   491  			stats.Epilogue++
   492  		}
   493  	}
   494  
   495  	stats.Uncovered = stats.Instructions - stats.Covered
   496  	stats.Coverage = stats.Covered * 100 / stats.Instructions
   497  	return stats
   498  }
   499  
   500  func (cfg *Cfg) PrintAnlyState() {
   501  	//	es := make([]edge, len(edges))
   502  	//	copy(es, edges)
   503  	//	sortEdges(es)
   504  
   505  	var badJumpList []string
   506  	for pc, stmt := range cfg.Program.Stmts {
   507  		if stmt.inferredAsData {
   508  			//fmt.Printf("data: %v\n", stmt.inferredAsData)
   509  			continue
   510  		}
   511  
   512  		if stmt.opcode == JUMPDEST {
   513  			fmt.Println()
   514  		}
   515  
   516  		var valueStr string
   517  		if stmt.opcode.IsPush() {
   518  			valueStr = fmt.Sprintf("%v %v", stmt.opcode, stmt.value.Hex())
   519  		} else {
   520  			valueStr = fmt.Sprintf("%v", stmt.opcode)
   521  		}
   522  
   523  		pc0s := make([]string, 0)
   524  		for pc0 := range cfg.PrevEdgeMap[pc] {
   525  			pc0s = append(pc0s, strconv.Itoa(pc0))
   526  		}
   527  
   528  		if cfg.BadJumps[pc] {
   529  			out := fmt.Sprintf("[%5v] (w:%2v) %3v\t %-25v %-10v %v\n", cfg.D[pc].anlyCounter, cfg.D[pc].worklistLen, pc, valueStr, strings.Join(pc0s, ","), cfg.D[pc].String(false))
   530  			fmt.Print(out)
   531  			badJumpList = append(badJumpList, out)
   532  		} else if cfg.PrevEdgeMap[pc] != nil {
   533  			fmt.Printf("[%5v] (w:%2v) %3v\t %-25v %-10v %v\n", cfg.D[pc].anlyCounter, cfg.D[pc].worklistLen, pc, valueStr, strings.Join(pc0s, ","), cfg.D[pc].String(true))
   534  		} else {
   535  			fmt.Printf("[%5v] (w:%2v) %3v\t %-25v\n", cfg.D[pc].anlyCounter, cfg.D[pc].worklistLen, pc, valueStr)
   536  		}
   537  	}
   538  
   539  	print("\nFull list of bad jumps:\n")
   540  	for _, badJump := range badJumpList {
   541  		fmt.Println(badJump)
   542  	}
   543  
   544  	g := dot.NewGraph(dot.Directed)
   545  	block2node := make(map[*Block]*dot.Node)
   546  	for _, block := range cfg.Program.Blocks {
   547  		n := g.Node(fmt.Sprintf("%v\n%v", block.Entrypc, block.Exitpc)).Box()
   548  		block2node[block] = &n
   549  	}
   550  
   551  	for pc1 := range cfg.PrevEdgeMap {
   552  		for pc0 := range cfg.PrevEdgeMap[pc1] {
   553  			block1 := cfg.Program.Entry2block[pc1]
   554  
   555  			if block1 == nil {
   556  				continue
   557  			}
   558  
   559  			block0 := cfg.Program.Exit2block[pc0]
   560  			if block0 == nil {
   561  				continue
   562  			}
   563  
   564  			n0 := block2node[block0]
   565  			n1 := block2node[block1]
   566  			g.Edge(*n0, *n1)
   567  		}
   568  	}
   569  
   570  	path := "cfg.dot"
   571  	_ = os.Remove(path)
   572  
   573  	f, errcr := os.Create(path)
   574  	if errcr != nil {
   575  		panic(errcr)
   576  	}
   577  	defer f.Close()
   578  
   579  	w := bufio.NewWriter(f)
   580  	_, errwr := w.WriteString(g.String())
   581  	if errwr != nil {
   582  		panic(errwr)
   583  	}
   584  	_ = w.Flush()
   585  }
   586  
   587  func (metrics *CfgMetrics) GetBadJumpReason() string {
   588  	if metrics.Valid {
   589  		return ""
   590  	}
   591  
   592  	if metrics.ShortStack {
   593  		return "ShortStack"
   594  	}
   595  
   596  	if metrics.AnlyCounterLimit {
   597  		return "AnlyCounterLimit"
   598  	}
   599  
   600  	if metrics.BadJumpImprecision {
   601  		return "Imprecision"
   602  	}
   603  
   604  	if metrics.BadJumpInvalidJumpDest {
   605  		return "InvalidJumpDest"
   606  	}
   607  
   608  	if metrics.BadJumpInvalidOp {
   609  		return "InvalidOpcode"
   610  	}
   611  
   612  	if metrics.Panic {
   613  		return "Panic"
   614  	}
   615  
   616  	return "Unknown"
   617  }
   618  
   619  func pushNewEdges(workList []edge, edges []edge) []edge {
   620  	for _, e := range edges {
   621  		inWorkList := false
   622  		for _, w := range workList {
   623  			if w.pc0 == e.pc0 && w.pc1 == e.pc1 {
   624  				inWorkList = true
   625  			}
   626  		}
   627  		if !inWorkList {
   628  			head := []edge{e}
   629  			workList = append(head, workList...)
   630  		}
   631  	}
   632  	return workList
   633  }
   634  
   635  func GenCfg(code []byte, anlyCounterLimit int, maxStackLen int, maxStackCount int, metrics *CfgMetrics) (cfg *Cfg, err error) {
   636  	program := toProgram(code)
   637  	cfg = &Cfg{Metrics: metrics}
   638  	cfg.BadJumps = make(map[int]bool)
   639  	cfg.Metrics = metrics
   640  	cfg.Program = program
   641  	cfg.PrevEdgeMap = make(map[int]map[int]bool)
   642  
   643  	startPC := 0
   644  	codeLen := len(program.Code)
   645  	cfg.D = make(map[int]*astate)
   646  	for pc := 0; pc < codeLen; pc++ {
   647  		cfg.D[pc] = emptyState()
   648  	}
   649  	cfg.D[startPC] = botState()
   650  
   651  	var workList []edge
   652  
   653  	edgesR1, errR1 := resolve(cfg, startPC)
   654  	if errR1 != nil {
   655  		return cfg, errR1
   656  	}
   657  	workList = pushNewEdges(workList, edgesR1)
   658  
   659  	for len(workList) > 0 {
   660  		if anlyCounterLimit > 0 && cfg.Metrics.AnlyCounter > anlyCounterLimit {
   661  			cfg.Metrics.AnlyCounterLimit = true
   662  			return cfg, errors.New("reached analysis counter limit")
   663  		}
   664  
   665  		var e edge
   666  		e, workList = workList[0], workList[1:]
   667  
   668  		preDpc0 := cfg.D[e.pc0]
   669  		preDpc1 := cfg.D[e.pc1]
   670  
   671  		post1, err := post(cfg, preDpc0, e, maxStackLen)
   672  		if err != nil {
   673  			return cfg, err
   674  		}
   675  
   676  		if !Leq(post1, preDpc1) {
   677  			postDpc1 := Lub(post1, preDpc1)
   678  			cfg.D[e.pc1] = postDpc1
   679  
   680  			if len(postDpc1.stackset) > maxStackCount {
   681  				//fmt.Printf("stacklen: %v %v\n", len(postDpc1.stackset), maxStackCount)
   682  				//fmt.Println(postDpc1.String(false))
   683  				//for _, stack := range postDpc1.stackset {
   684  				//	fmt.Printf("%v\n", stack.String(false))
   685  				//}
   686  				cfg.Metrics.StackCountLimitReached = true
   687  				return cfg, errors.New("stack count limit reach")
   688  			}
   689  
   690  			edgesR2, errR2 := resolve(cfg, e.pc1)
   691  			if errR2 != nil {
   692  				return cfg, errR2
   693  			}
   694  			workList = pushNewEdges(workList, edgesR2)
   695  		}
   696  
   697  		decp1Copy := cfg.D[e.pc1]
   698  		decp1Copy.anlyCounter = cfg.Metrics.AnlyCounter
   699  		decp1Copy.worklistLen = len(workList)
   700  		cfg.D[e.pc1] = decp1Copy
   701  		cfg.Metrics.AnlyCounter++
   702  
   703  		cfg.checkRep()
   704  	}
   705  
   706  	if len(cfg.BadJumps) > 0 {
   707  		cfg.Metrics.Unresolved = true
   708  		return cfg, errors.New("unresolvable jumps found")
   709  	}
   710  
   711  	cov := cfg.GetCoverageStats()
   712  	if cov.Uncovered > cov.Epilogue {
   713  		cfg.Metrics.LowCoverage = true
   714  		//return cfg, errors.New("low coverage")
   715  	}
   716  
   717  	cfg.Metrics.Valid = true
   718  
   719  	return cfg, nil
   720  }
   721  
   722  func (cfg *Cfg) GenerateProof() *CfgProof {
   723  	succEdgeMap := make(map[int][]int)
   724  	proof := CfgProof{}
   725  	entries := make(map[int]bool)
   726  	exits := make(map[int]bool)
   727  
   728  	pcs := make(map[int]bool)
   729  	for pc1, pc0s := range cfg.PrevEdgeMap {
   730  		for pc0 := range pc0s {
   731  			succEdgeMap[pc0] = append(succEdgeMap[pc0], pc1)
   732  			pcs[pc0] = true
   733  			pcs[pc1] = true
   734  		}
   735  	}
   736  
   737  	entries[0] = true
   738  	for pc := range pcs {
   739  		if pc == 0 || len(cfg.PrevEdgeMap[pc]) > 1 {
   740  			entries[pc] = true
   741  		}
   742  		for pc0 := range cfg.PrevEdgeMap[pc] {
   743  			if len(succEdgeMap[pc0]) > 1 {
   744  				entries[pc] = true
   745  				break
   746  			}
   747  		}
   748  		opcode := OpCode(cfg.Program.Code[pc])
   749  		if opcode == JUMPDEST {
   750  			entries[pc] = true
   751  		}
   752  		if opcode == JUMPI {
   753  			if pc < len(cfg.Program.Code)-1 {
   754  				entries[pc+1] = true
   755  			}
   756  		}
   757  	}
   758  
   759  	for pc0 := range pcs {
   760  		for _, pc1 := range succEdgeMap[pc0] {
   761  			if entries[pc1] {
   762  				exits[pc0] = true
   763  				break
   764  			}
   765  		}
   766  
   767  		if len(succEdgeMap[pc0]) == 0 || len(succEdgeMap[pc0]) > 1 {
   768  			exits[pc0] = true
   769  		}
   770  
   771  		opcode := OpCode(cfg.Program.Code[pc0])
   772  		if opcode == JUMP || opcode == JUMPI ||
   773  			opcode == RETURN || opcode == REVERT {
   774  			exits[pc0] = true
   775  		}
   776  	}
   777  
   778  	entriesList := make([]int, 0)
   779  	for pc := range entries {
   780  		entriesList = append(entriesList, pc)
   781  	}
   782  	sort.Ints(entriesList)
   783  	for _, pc0 := range entriesList {
   784  		pc1 := pc0
   785  		for !exits[pc1] {
   786  			if len(succEdgeMap[pc1]) != 1 {
   787  				panic("Inconsistent successors")
   788  			}
   789  			pc1 = succEdgeMap[pc1][0]
   790  		}
   791  
   792  		block := CfgProofBlock{}
   793  		block.Entry = &CfgProofState{pc0, StringifyAState(cfg.D[pc0])}
   794  		block.Exit = &CfgProofState{pc1, StringifyAState(cfg.D[pc1])}
   795  		proof.Blocks = append(proof.Blocks, &block)
   796  	}
   797  
   798  	for _, predBlock := range proof.Blocks {
   799  		for _, succBlock := range proof.Blocks {
   800  			if cfg.PrevEdgeMap[succBlock.Entry.Pc][predBlock.Exit.Pc] {
   801  				predBlock.Succs = append(predBlock.Succs, succBlock.Entry.Pc)
   802  				succBlock.Preds = append(succBlock.Preds, predBlock.Exit.Pc)
   803  			}
   804  		}
   805  	}
   806  
   807  	return &proof
   808  }