2

I'm trying to write a program which concurrently mines a block of bitcoin. I have set it up so that each goroutine has an initial starting nonce each of which is a fraction of 4 ie. 2**64 - 1 (max number of uint64 type) / 1 or 2 or 3 or 4.

Only one of these miners will come across the correct nonce and when that happens I would like it to pass it to the miner manager through a channel and when this happens I would like the other 3 miners to stop what they're doing.

The only problem is I have no idea how to destroy a running goroutine, or if there is even a way to do what I'm asking.

func miner(blockNumber int, transactions string, previousHash string, zeroPrefix string, startNonce uint64, nonceChan chan uint64, hashChan chan string) {
    var text string
    var newHash string

    for {
        text = strconv.Itoa(blockNumber) + transactions + previousHash + strconv.FormatUint(startNonce, 10)
        newHash = encrypt(text)

        if startswith(newHash, zeroPrefix) {
            nonceChan <- startNonce
            hashChan  <- newHash

            close(nonceChan)
            close(hashChan)
            break
        } else {
            startNonce++
        }
    }
}

func mine(blockNumber int, transactions string, previousHash string, zeroPrefix int) Block {
    var prefixString string
    var newHash string
    var nonce uint64
    var startNonce uint64

    nonceChan := make(chan uint64)
    hashChan := make(chan string)

    for i := 0; i < zeroPrefix; i++ {
        prefixString += "0"
    }

    start := time.Now()

    for i := 0; i < 4; i++{
        // This line is for deciding at what nonce value a miner should start at.
        startNonce = uint64((float64(i) / 4) * math.Pow(2, 64))

        go func() {
            fmt.Println("Started miner with start nonce of", startNonce)
            miner(blockNumber, transactions, previousHash, prefixString, startNonce, nonceChan, hashChan)
        }()
    }

    nonce = <- nonceChan
    newHash = <- hashChan

    // Here is where I would like to destroy the other three miners

    block := Block{
        blockNumber,
        transactions,
        previousHash,
        newHash,
        nonce,
        zeroPrefix,
        time.Since(start),
    }

    return block
}
Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189

2 Answers2

4

Create a ctx, cancel := context.WithCancel(context.Background()) in the function that starts all goroutines and pass it to all goroutines (as first param in function).

When work should be cancelled, call cancel function. You can do this e.g. in the main function after receiving the result.

In each goroutine check with a select (in your for loop) for ctx.Done:

select {
case <-ctx.Done():
    return
default:
}

// continue mining

Example:

func miner(ctx context.Context, ...) {
    defer func() {
        // any necessary cleanup
    }

    for {
        select {
        case <-ctx.Done():
            // abort was called for: exit
            return
        default:
        }

        // continue mining
    }
}
func mine() {
    // use a single channel to get the result. You could 
    // block yourself if you use multiple channels
    chResult := make(chan result)

    // create context
    ctx, cancel := context.WithCancel(context.Background())

    for i := 0; i < 4; i++{
        // ...

        // pass the context into the miner
        go miner(ctx, chResult, ...)
    }

    // block for first miner to be successful
    res := <-chResult
   
    // cancel the other routines
    cancel()
    
    // ...
}

result could be:

struct result {
    hash  string
    nonce uint64
}
TehSphinX
  • 6,536
  • 1
  • 24
  • 34
3

you can use context which is one of the typical object to handle go routine's termination.

context : Package context defines the Context type, which carries deadlines, cancellation signals, and other request-scoped values across API boundaries and between processes.

ctx, cancel := context.WithCancel(context.Background()) with this you can create a context with cancel func.

Just pass ctx and cancel in your go routine and when you are done inside any of your go routines , Just make the cancel() func() call. and ctx.done() will be then true and then first case of switch will be true and it will return from all of your go routines.


func miner( ctx context.Context,  blockNumber int, transactions string, previousHash string, zeroPrefix string, startNonce uint64, nonceChan chan uint64, hashChan chan string) {
    var text string
    var newHash string

    for {
        select {
        case <-ctx.Done():  // if cancel() execute
           return
        default:
            text = strconv.Itoa(blockNumber) + transactions + previousHash + strconv.FormatUint(startNonce, 10)
            newHash = encrypt(text)

            if startswith(newHash, zeroPrefix) {
                nonceChan <- startNonce
                hashChan  <- newHash

                close(nonceChan)
                close(hashChan)
                break
            } else {
                startNonce++
            }

        }

    }
}

func mine(blockNumber int, transactions string, previousHash string, zeroPrefix int) Block {
    var prefixString string
    var newHash string
    var nonce uint64
    var startNonce uint64

    nonceChan := make(chan uint64)
    hashChan := make(chan string)

    for i := 0; i < zeroPrefix; i++ {
        prefixString += "0"
    }

    start := time.Now()
    ctx, cancel := context.WithCancel(context.Background())

    for i := 0; i < 4; i++{
        // This line is for deciding at what nonce value a miner should start at.
        startNonce = uint64((float64(i) / 4) * math.Pow(2, 64))

        go func(ctx context.Context) {
            fmt.Println("Started miner with start nonce of", startNonce)
            miner(ctx, blockNumber, transactions, previousHash, prefixString, startNonce, nonceChan, hashChan)
        }(ctx)
    }

    nonce = <- nonceChan
    newHash = <- hashChan
    cancel()

    // Here is where I would like to destroy the other three miners

    block := Block{
        blockNumber,
        transactions,
        previousHash,
        newHash,
        nonce,
        zeroPrefix,
        time.Since(start),
    }

    return block
}
Emon46
  • 1,506
  • 7
  • 14