func runNet(minerCount int, blockTime int, 
			timeSpan int, stepInterval int, 
			runDuration int, 
			output string, 
			rankdir string, 
			keepLogs bool) (string, error) {
	var miners []*rpctest.Harness
	var err error
	extraArgs := []string{}
	dagNetParams := chaincfg.SimNetParams
	if (timeSpan != 0) {
		dagNetParams.TargetTimespan = time.Second * time.Duration(timeSpan)
	}
	if (blockTime != 0) {
		dagNetParams.TargetTimePerBlock = time.Millisecond * time.Duration(blockTime)
	}
	for i := 0; i < minerCount; i++ {
		miner, err := rpctest.New(&dagNetParams, nil, extraArgs, keepLogs)
		if err != nil {
			return "", fmt.Errorf("unable to create mining node %d: %s", i, err)
		}
		if err := miner.SetUp(false, 0); err != nil {
			return "", fmt.Errorf("unable to complete mining node %d setup: %s", i, err)
		}
		if keepLogs {
			fmt.Printf("miner %d log dir: %s\n", i, miner.LogDir())
		}
		miners = append(miners, miner)
	}
	defer func() {
		for _, miner := range miners {
			_ = (*miner).TearDown()
		}
	}()
	err = rpctest.ConnectNodes(miners)
	if err != nil {
		return "", fmt.Errorf("unable to connect nodes: %s", err)
	}
	for i, miner := range miners {
		err := miner.Node.SetGenerate(true, 1)
		if err != nil {
			fmt.Printf("failed to start miner on node %v: %v\n", i, err)
		}
	}
	var stepDots [][]byte
	stepCount := 0
	if (stepInterval != 0) {
		timeStart := time.Now() 
		for {
			fmt.Println("Generating Step", stepCount)
			dot, err := rpctest.RenderDagsDot(miners, rankdir)
			if err != nil {
				return "", fmt.Errorf("failed to render dag in graphviz DOT format: %s", err)
			}
			stepDots = append(stepDots, dot)
			timeNow := time.Now()
			timeDuration := time.Duration(runDuration) * time.Second
			time.Sleep(time.Duration(stepInterval) * time.Millisecond)
			fmt.Printf("Generating for %v\n", timeNow.Sub(timeStart))
			if (timeNow.Sub(timeStart) > timeDuration) {
				break
			}
			stepCount++
		}
	} else {
		time.Sleep(time.Duration(runDuration) * time.Second)
	}
	for i, miner := range miners {
		err := miner.Node.SetGenerate(false, 0)
		if err != nil {
			fmt.Printf("failed to stop miner on node %v: %v\n", i, err)
		}
	}
	fmt.Println("Finalizing")
	dot, err := rpctest.RenderDagsDot(miners, rankdir)
	if err != nil {
		return "", fmt.Errorf("failed to render dag in graphviz DOT format: %s", err)
	}
	stepDots = append(stepDots, dot)
	var outDir string
	if len(output) == 0 {
		outDir, err = ioutil.TempDir("", "dagviz")
	} else {
		info, pathErr := os.Stat(output)
		if pathErr == nil && info.IsDir() {
			outDir = output
		} else {
			err = os.MkdirAll(output, 0755)
		}
	}
	if err != nil {
		return "", err
	}
	for step := 0; step <= stepCount; step++ {
		fmt.Println("Rendering Step", step)
		dot := stepDots[step]
		svg, err := soterutil.DotToSvg(dot)
		if err != nil {
			return "", fmt.Errorf("failed to convert DOT file to SVG: %s", err)
		}
		svgEmbed, err := soterutil.StripSvgXmlDecl(svg)
		if err != nil {
			return "", fmt.Errorf("failed to strip xml declaration from SVG image: %s", err)
		}
		svgBody, err := soterutil.RenderSvgHTMLFigure(svgEmbed)
		h, err := soterutil.RenderSteppingHTML(svgBody, "dag", step-1, step+1)
		if err != nil {
			return "", fmt.Errorf("failed to render SVG image as HTML: %s", err)
		}
		pattern := "dag_" + strconv.Itoa(step) + ".html"
		name := filepath.Join(outDir, pattern)
		fh, err := os.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
		if err != nil {
			return "", fmt.Errorf("failed to create output file-handle: %s", err)
		}
		err = saveHTML(h, fh)
		if err != nil {
			return "", fmt.Errorf("failed to save HTML file: %s", err)
		}
	}
	return outDir + "/dag_0.html", nil
}
runNet runs a network of miners, generates some blocks on them, taking snapshots at an interval
and renders the dag as html at each interval
package main
import (
	"flag"
	"fmt"
	"github.com/soteria-dag/soterd/soterutil"
	"io/ioutil"
	"os"
	"path/filepath"
	"strconv"
	"syscall"
	"time"
	"github.com/soteria-dag/soterd/chaincfg"
	"github.com/soteria-dag/soterd/integration/rpctest"
)
func saveHTML(bytes []byte, fh *os.File) error {
	_, err := fh.Write(bytes)
	if err != nil {
		return err
	}
	err = fh.Close()
	if err != nil {
		return err
	}
	return nil
}