Author

Topic: TerraCoin (Not NovaCoin/PPCoin) exploit: difficulty/reward coaster (Read 3540 times)

legendary
Activity: 1344
Merit: 1001
Interesting...
legendary
Activity: 1204
Merit: 1002
RUM AND CARROTS: A PIRATE LIFE FOR ME
Is this a problem with Terracoin? It was my understanding that the retarget rate is so much quicker that it would settle pretty quickly once you jumped off the chain.

The shorter the retarget time, the more susceptible a chain is to this sort of exploitation; for TRC you would just adjust the code above to mine TRC for 30 blocks, mine something else for the next 30 blocks, then hop onto TRC again.  This is why retarget times should NEVER be set too short.  The code (I guess) would be
Code:
floor((blockheight) / 30) % 2 == 0

I was mistaken as thought that PPC retargeted per block with a small maximum decrease or increase, but it actually adjusts difficulty based on the rolling exponential average for the past 1008 blocks.

We are talking about the pros'/cons' of this over here: https://bitcointalk.org/index.php?topic=157449.new#new
legendary
Activity: 1484
Merit: 1005
Is this a problem with Terracoin? It was my understanding that the retarget rate is so much quicker that it would settle pretty quickly once you jumped off the chain.

The shorter the retarget time, the more susceptible a chain is to this sort of exploitation; for TRC you would just adjust the code above to mine TRC for 30 blocks, mine something else for the next 30 blocks, then hop onto TRC again.  This is why retarget times should NEVER be set too short.  The code (I guess) would be
Code:
floor((blockheight) / 30) % 2 == 0

I was mistaken as thought that PPC retargeted per block with a small maximum decrease or increase, but it actually adjusts difficulty based on the rolling exponential average for the past 1008 blocks.
legendary
Activity: 1204
Merit: 1002
RUM AND CARROTS: A PIRATE LIFE FOR ME
Block reward is a function of difficulty, nothing to do with block height.

Sure you can mine 2 days and push up the difficulty, then stop. Mind you this is an existing problem with all bitcoin forks. Even litecoin is subject to huge oscillations of difficulty in 50% range. The continuous adjustment alleviates this problem, by immediately raising difficulty gradually, also for ppcoin it also lowers the block subsidy in the process. So it is actually less advantageous for the strategy.

Is this a problem with Terracoin? It was my understanding that the retarget rate is so much quicker that it would settle pretty quickly once you jumped off the chain.
legendary
Activity: 1484
Merit: 1005
Right.  Okay, I'll try and fix this by the end of the week.  The neat thing about PPC/NVC is that it helps with hopping between chains because there are no present difficulty change times, so you can just hop on at any time to the chain and begin ramping up the difficulty.  This is in contrast to BTC or LTC in which difficulty adjustments occur at fixed periods.  Because of the fixed periods, it'd be more difficult to get the chains to coincide with one another.  Thus, per-block difficulty adjustment seems to encourage block chain hopping, especially as the number of chains exist that use it increase.

Anyway, it'd go something like this:

Mine NVC for 288 blocks
Mine PPC for 288 blocks
Wait for LTC to adjust difficulty, then hop on to that chain and mine for 2016 blocks
if (NVC has mined at least 720 blocks since) mine for 288 blocks else wait
if (PPC has mined at least 720 blocks since) mine for 288 blocks else wait
Wait for LTC to adjust difficulty, then hop on to that chain and mine for 2016 blocks
etc

The more chains there are the greater benefit to the person manipulating the difficulty, especially if these chains have per-block difficulty adjustments because the amount of time spent waiting is decreased
legendary
Activity: 1205
Merit: 1010
Block reward is a function of difficulty, nothing to do with block height.

Sure you can mine 2 days and push up the difficulty, then stop. Mind you this is an existing problem with all bitcoin forks. Even litecoin is subject to huge oscillations of difficulty in 50% range. The continuous adjustment alleviates this problem, by immediately raising difficulty gradually, also for ppcoin it also lowers the block subsidy in the process. So it is actually less advantageous for the strategy.
legendary
Activity: 1484
Merit: 1005
Okay, so weighting factors decrease exponentially, that is, the farther towards the end of the week the values go from the present, the less they influence the difficulty.  So, a possible attack would be to mine with high hash rate for 2 days, wait 5 days until the high hash rate has barely any impact on the present difficulty, and repeat?

Additionally, because of
Code:
// ppcoin: subsidy is cut in half every 16x multiply of difficulty
, won't the benefit to the attacker to mine every nth block be exponential and increasing with the gap between number of blocks (as n increases) because of the subsidy calculation as above?  For instance, if you adjust line
Code:
if (output_j['blocks'] % 2 == 0):
to
Code:
if (output_j['blocks'] % 16 == 0):
, the attacker with a ton of hashing power likely gets every 16th block for very little overall work and their influence on subsidy and difficulty is negligible; not only this, but if this person had a massive quantity of hash power (e.g. a few ASICs right now) the benefit to the attacker in terms of work it takes to acquire PPC would be exponential because of the increase in block reward as compared to what the block reward would be if they were continually mining.

edit: Sorry, made lots of edits for clarity/typos.  I agree that the exploit as posted in the OP doesn't work as stated because of how I understood your adjustment algorithm, but it should work by mining for 288 PPC/NVC blocks followed by mining something else for 720 PPC/NVC blocks and then hopping back on the chain.  I guess this is more analogous to pool hopping than anything else.
legendary
Activity: 1205
Merit: 1010
edit:  Even if you base it on a one week moving average, with 50% of the network running the script wouldn't you achieve a disproportionate increase in block reward per amount of work required to find a block?

Not much, as say you mine odd block average every 5 minutes, even block average 15 minutes. The difficulty would remain relatively stable overall, but the average difficulty of odd blocks would be smaller than those of even blocks because of continuous adjustment (this is true for any continuous adjustment e.g. devcoin). But it's only slightly lower (say 1% magnitude) if the sampling interval is long enough.
legendary
Activity: 1205
Merit: 1010
Code:
static const int64 nTargetTimespan = 7 * 24 * 60 * 60; // one week

You can read https://en.wikipedia.org/wiki/Exponential_moving_average#Exponential_moving_average for the concept of exponential moving average.

I used a variation of it for ppcoin's difficulty adjustment.
legendary
Activity: 1484
Merit: 1005
Maybe I misunderstood your code; retargets are based on a one week moving average?

This is from your main.cpp:
Code:
   // ppcoin: target change every block
    // ppcoin: retarget with exponential moving toward target spacing
    CBigNum bnNew;
    bnNew.SetCompact(pindexPrev->nBits);
    int64 nTargetSpacing = fProofOfStake? STAKE_TARGET_SPACING : min(nTargetSpacingWorkMax, (int64) STAKE_TARGET_SPACING * (1 + pindexLast->nHeight - pindexPrev->nHeight));
    int64 nInterval = nTargetTimespan / nTargetSpacing;
    bnNew *= ((nInterval - 1) * nTargetSpacing + nActualSpacing + nActualSpacing);
    bnNew /= ((nInterval + 1) * nTargetSpacing);

    if (bnNew > bnProofOfWorkLimit)
        bnNew = bnProofOfWorkLimit;

    return bnNew.GetCompact();

It's a little convoluted, help me out?

edit:  Even if you base it on a one week moving average, with 50% of the network running the script wouldn't you achieve a disproportionate increase in block reward per amount of work required to find a block?
legendary
Activity: 1205
Merit: 1010
How much advantage are you expecting exactly with this 'exploit' say if you have 50% of network hash power to collude?

The difficulty is computed based on a one-week moving average so it should be reasonably stable (comparable to litecoin's 3.5 day average).

I'd say it's more of a problem with bitcoin's algorithm demonstrated clearly by freicoin and many other altcoins before it (including litecoin). Mine the heck with low difficulty for a day then stop for 3 months. Sure enough you can write a script for terracoin to take advantage of its difficulty jumps as well, likely gaining more advantage than the scheme you described here for PPC/NVC.
legendary
Activity: 1484
Merit: 1005
Brief description of the exploit
Difficulty and reward adjust per block for NovaCoin and PPCoin (see main.cpp).  By mining every other block and having a large hash power (ideally as large as possible), difficulty may be kept low and reward may be kept high for the person with the high hash power.  This is because there is a maximum cap on difficulty change for each round.  The higher the hashing power, the more the exploiter benefits.

Code (Unix, python 2.7, Novacoin targeting)
Code:
from subprocess import Popen, PIPE, STDOUT
import time
import json

class Nova(object):
    def __init__(self, blocks):
        self.blocks = blocks

scan_time = 5

while(True):
    ### Get the initial block number from novacoind, store in output_j['blocks']
    cmd = './novacoind getinfo' ### Command for dumping info from novacoind in json format; we use this lots
    p = Popen(cmd, shell=True, stdin=PIPE, stdout=PIPE, stderr=STDOUT, close_fds=True) ### Execute command
    output = p.stdout.read() ### Read the stdout that output gives us
    output_j = json.loads(output) ### Convert from json to python values; we call block number using the class given above
    print("Current block is " + str(output_j['blocks']))

    if (output_j['blocks'] % 2 == 0): ### Block is even, mine NovaCoin
        print ("Even block, mining NVC...")

        novacoin_config = open('novamine.conf','r')
        card_config = open('cardconf.conf','r')
        configuration = open('litecoin.conf', 'w')

        ### Copy the reaper configuration to litecoin.conf for novacoin
        for line in novacoin_config:
            configuration.write(line)
        for line in card_config:
            configuration.write(line)

        novacoin_config.close()
        card_config.close()
        configuration.close()

        miner_cmd = './reaper' ### Command for your miner to mine NVC
        miner_thread = Popen(miner_cmd, shell=True, stdin=PIPE, stdout=PIPE, stderr=STDOUT, close_fds=True)

        ### Loop below checks to see when block we're on, terminates miner if we are on an off number block
        while(True):
            p = Popen(cmd, shell=True, stdin=PIPE, stdout=PIPE, stderr=STDOUT, close_fds=True)
            output = p.stdout.read()
            output_j = json.loads(output)
            if not(output_j['blocks'] % 2 == 0):
                miner_thread.terminate()
                break
            else:
                print("Block still even, mining novacoin.")
                time.sleep(scan_time)

    else: ### Block is odd, mine something else
        print ("Odd block, mining another chain...")

        novacoin_config = open('elsemine.conf','r')
        card_config = open('cardconf.conf','r')
        configuration = open('litecoin.conf', 'w')

        ### Copy the reaper configuration to litecoin.conf for other chain
        for line in novacoin_config:
            configuration.write(line)
        for line in card_config:
            configuration.write(line)

        novacoin_config.close()
        card_config.close()
        configuration.close()

        miner_cmd = './reaper' ### Command for your miner to mine something else; the same here because we use reaper
        miner_thread = Popen(miner_cmd, shell=True, stdin=PIPE, stdout=PIPE, stderr=STDOUT, close_fds=True)

        ### Loop below checks to see when block we're on, terminates miner if we are on an even number block
        while(True):
            p = Popen(cmd, shell=True, stdin=PIPE, stdout=PIPE, stderr=STDOUT, close_fds=True)
            output = p.stdout.read()
            output_j = json.loads(output)
            if (output_j['blocks'] % 2 == 0):
                miner_thread.terminate()
                break
            else:
                print("Block still odd, mining another chain.")
                time.sleep(scan_time)

Code (Windows, python 2.7, Novacoin targeting)
Code:
from subprocess import Popen, PIPE, STDOUT
import time
import json

class Nova(object):
    def __init__(self, blocks):
        self.blocks = blocks

scan_time = 5

while(True):
    ### Get the initial block number from novacoind, store in output_j['blocks']
    cmd = 'novacoind.exe getinfo' ### Command for dumping info from novacoind in json format; we use this lots
    p = Popen(cmd, shell=True, stdin=PIPE, stdout=PIPE, stderr=STDOUT, close_fds=True) ### Execute command
    output = p.stdout.read() ### Read the stdout that output gives us
    output_j = json.loads(output) ### Convert from json to python values; we call block number using the class given above
    print("Current block is " + str(output_j['blocks']))

    if (output_j['blocks'] % 2 == 0): ### Block is even, mine NovaCoin
        print ("Even block, mining NVC...")

        novacoin_config = open('novamine.conf','r')
        card_config = open('cardconf.conf','r')
        configuration = open('litecoin.conf', 'w')

        ### Copy the reaper configuration to litecoin.conf for novacoin
        for line in novacoin_config:
            configuration.write(line)
        for line in card_config:
            configuration.write(line)

        novacoin_config.close()
        card_config.close()
        configuration.close()

        miner_cmd = 'reaper.exe' ### Command for your miner to mine NVC
        miner_thread = Popen(miner_cmd, shell=True, stdin=PIPE, stdout=PIPE, stderr=STDOUT, close_fds=True)

        ### Loop below checks to see when block we're on, terminates miner if we are on an off number block
        while(True):
            p = Popen(cmd, shell=True, stdin=PIPE, stdout=PIPE, stderr=STDOUT, close_fds=True)
            output = p.stdout.read()
            output_j = json.loads(output)
            if not(output_j['blocks'] % 2 == 0):
                miner_thread.terminate()
                break
            else:
                print("Block still even, mining novacoin.")
                time.sleep(scan_time)

    else: ### Block is odd, mine something else
        print ("Odd block, mining another chain...")

        novacoin_config = open('elsemine.conf','r')
        card_config = open('cardconf.conf','r')
        configuration = open('litecoin.conf', 'w')

        ### Copy the reaper configuration to litecoin.conf for other chain
        for line in novacoin_config:
            configuration.write(line)
        for line in card_config:
            configuration.write(line)

        novacoin_config.close()
        card_config.close()
        configuration.close()

        miner_cmd = 'reaper.exe' ### Command for your miner to mine something else; the same here because we use reaper
        miner_thread = Popen(miner_cmd, shell=True, stdin=PIPE, stdout=PIPE, stderr=STDOUT, close_fds=True)

        ### Loop below checks to see when block we're on, terminates miner if we are on an even number block
        while(True):
            p = Popen(cmd, shell=True, stdin=PIPE, stdout=PIPE, stderr=STDOUT, close_fds=True)
            output = p.stdout.read()
            output_j = json.loads(output)
            if (output_j['blocks'] % 2 == 0):
                miner_thread.terminate()
                break
            else:
                print("Block still odd, mining another chain.")
                time.sleep(scan_time)

Requires the following files to exist:
novacoind (or ppcoind) executable that is running prior to executing the python code above
reaper executable
reaper.conf (configured as per normal)
novamine.conf:
Code:
host novacoinhost
port ????
user username
pass password
elsemine.conf (mines litecoin here, could be anything):
Code:
host litecoinhost
port ????
user username
pass password
cardconf.conf (configuration for scrypt):
Code:

protocol litecoin

worksize 256
vectors 1
aggression 20
threads_per_gpu 1
sharethreads 32
lookup_gap 2
gpu_thread_concurrency ####

Notes
- The exploit is made doubly beneficial by inducing both decreased difficulty and increased block reward for the attacker, as per NVC/PPCs algorithms for adjustment
- Solo miners with high hash rates especially benefit
- The attack is easily identified by watching the network block rate and difficulty/block reward
- The attack can be collusional; if lots of miners run it, it benefits all of them by giving them more coins for less electricity
- The attack should also work on round-based proportional pay-per-share pools
- The attack screws up PPS pools that are smaller than the attacker's hash power, because the pool will likely only get every other block
- Such an attack is avoidable by making retarget based on a larger span of blocks, hence why bitcoin chose two week retarget times
- The attack has previously been observed on the litecoin network, but is especially easy to implement here because of per-block retargeting
- To use cgminer, simply put 'cgminer --your_args' in the lines labeled: miner_cmd = 'whatever'
Jump to: