Skip to content

Instantly share code, notes, and snippets.

@Ayms

Ayms/ub.md Secret

Last active February 22, 2018 22:03
Show Gist options
  • Save Ayms/86309b48084087a82634615d209f8b26 to your computer and use it in GitHub Desktop.
Save Ayms/86309b48084087a82634615d209f8b26 to your computer and use it in GitHub Desktop.
United Bitcoin and the God mode or The robbery of the century

United Bitcoin and the God mode or The robbery of the century

What UB did, is doing, will do and associated (very) dangerous things

What happened?

In what follows, for public key we mean public key or redeem script, let's take the simple case p2pkh but the same applies for p2sh

  • Phase 1: block 498777 the 11th Dec 2017

"Addresses that had an activity 30 days before will be credited the same amount of UBTC"

It's unclear here what they mean by "activity", probably they meant "outgoing" as explained below, this seems somewhere absurd because then you don't have the coins on the same address, this can explain why everybody missed phase 1 and why there is phase 2

But let's see a little bit in the past: https://web.archive.org/web/20171215055312/https://www.ub.com/project/get

"Eligibility

All users who transfer Bitcoins from his/her own address to his/her own address between Block 494000 and Block 498777 (11 November 2017 to 12 December 2017 GMT) will be eligible if the transaction meets the following criteria.

The output address (receiving address) must also be listed as one of the input addresses and cannot be a totally new address

The output address (receiving address) must end up with a balance of more than 0.01 BTC

If you are not sure if the operation is complete, please make two transfers."

And they continue with some very good advice that are certainly not dangerous for people:

"a) If your wallet contains a large number of addresses, please send all your BTC to one address first; then transfer a fraction to the address itself (0.0001 BTC is ok)

b) If you don’t know your sending address(es), create a new address and transfer all your Bitcoins to it; then transfer a fraction to the address itself (0.0001 BTC is ok)"

In short it means that the active address must have received some coins but also spent some during the period and the total owned by the address must be > 0.01

So if you sent N (ie all of your) BTCs to an address with N>0.01 then you should have sent N BTCs again from that address to the same one, or 0.01 to that address, the rest to another one, and from this one back to the initial address, or x time some parts of N to that address, and then just spend one small part, etc

Yes, this looks quite stupid but that's their process, please see why below

  • Phase2: until 3rd January, eligible addresses are:

did not receive UBTC in the first round; and had a Bitcoin balance at block 498,777 (approximately UTC +0 midnight 12th December 2017)higher than 0.01 BTC; and had activity (outgoing) between block 494,000 and 502,315 (around 12th November 2017 to 3rd January 2018);

  • outgoing means now that for the address mentionned above you need to spend it on BTC, because if you did not get anything for phase 1 it is because you did not do it during phase 1, luckily you are now authorized to transfer your coins to another address, not the same one

  • therefore the activity required for both phases is to allow the UBTC team to retrieve the public key corresponding to your address, which is one of the validation step insuring that a transaction is correct (the second one being the signature verification)

  • indeed, as a reminder, your address is the hash of your public key, you can't get the public key from it, but the public key is required to check a transaction, therefore it becomes public when you spend an output corresponding to that address (outgoing), you can still reuse this address after since people can't sign the transaction without knowing the private key that you are the only one to have, but still one of the two verification steps becomes useless

  • then they were supposed to use it to create a transaction in UBTC to send your coins to you, see how below

  • apparently there was no phase 1 at all, because nobody understood what they meant and everybody just made sure to have some activity during the period with one address but without any spent transaction from this address to meet the requirements, so probably a very few addresses were concerned, as a matter of fact we have not found any so far, the least that we could say is that UB process did not help any "distribution" during phase 1

  • so almost everything got transferred to 31rZdrTpN57Wbfhg7xTPxeFGjEQaMBjxoo

"A private miner will validate the transfer of the remaining unclaimed UnitedBitcoin to multiple multi-signature addresses controlled by the UB Foundation."

  • 31rZdrTpN57Wbfhg7xTPxeFGjEQaMBjxoo (the private miner) is a miner belonging to the UBTC team and hard coded in chainparams.cpp as we can see below, as well as a "generator" public key, called the "generator" hereafter

  • the balances of quasi all addresses before the fork in UBTC are 0 or very small, everything went to the miner

  • then it becomes obvious that they used the God mode (see in the code) to create transactions to spend the outputs whether directly to miner 31rZdrTpN57Wbfhg7xTPxeFGjEQaMBjxoo or with zero or very small values so everything got transferred to miner 31rZdrTpN57Wbfhg7xTPxeFGjEQaMBjxoo as network fees

Indeed the UB explorer is unclear, for example:

https://www.ub.com/explorer/address?address=1F34duy2eeMz5mSrvFepVzy7Y1rBsnAyWC gives a very small balance on UBTC but does not show any transaction on UB network

but

https://www.ub.com/explorer/address?address=13MAXoPLQEmrtSTASDZrGNNpmsZ1gsAUnn gives a 0 balance on UBTC and show a transaction spending the UBTCs to the miner, and mysteriously this transaction disappeared from the explorer some time ago, then reappeared

  • as we can see below they skipped in the code (EvalScript) the signature verification that they replaced by a custom one in God mode where the signature performed by the generator is checked there (probably to avoid that others can do the same during the God mode, ie make transactions to send coins to themselves)

  • for the address check they replaced in scriptpubkey the original pubkey by the one of the generator, but there is a check that seems to suggest that they check the pubkey of the generator OR the correct one (that they got if you had an activity in phase 1, probably to transfer the coins to the addresses fulfilling their requirements)

  • they did this during the God mode between block 498777 (11 Dec 2017) and 499277 (14 Dec 2017)

  • anyway it is useless to really understand what they did in fact, they can do whatever they like in a "God mode"

  • since miner 31rZdrTpN57Wbfhg7xTPxeFGjEQaMBjxoo got everything, they transferred the coins to other addresses that they own

  • the conclusion so far is that they transferred quasi everything before block 498777 to themselves, which explain why nobody succeeded to claim their UBTC coins, nothing can be claimed since there is nothing left, only the UBTC team can send the coins to the addresses that fulfill what was required for phase 2 (please see below)

  • probably they got lucky that nobody checked the code before and applied the God mode to himself, even if they included some protections and even if we can suspect that they did this inside a private network so nobody could interfer with this process

  • we are not sure about everything since we don't have any hex dump of any transaction but it's difficult to envision a different scenario given the facts

How can/will UBTC team send you your coins and when?

In the process they still want your public key and now are asking you to register to get your coins ("grace" period after phase2)

  • When: when they like, they do it at a very slow rate and starting by the lowest amounts, so people don't screw up the price by selling all of their UBTCs (please see https://bitcointalk.org/index.php?topic=2635759.msg30577788#msg30577788), that's why they asked people to send everything to one single address, people should not have followed that advice because they could still have their coins on many addresses and fulfill their requirements instead of being waiting for them to get their coins.

That's not all, the registration process looks quite dangerous, because, in addition, now everybody can track you, especially the UB team, you can register with dummy parameters but this does not protect you from being discovered if needed (by some authorities or whatever)

And if you did not register it's not unlikely at all that you contacted the UB support giving them details about your addresses to know when you will get your coins, then the same applies

  • How:

"validate the transfer of the remaining unclaimed UnitedBitcoin to multiple multi-signature addresses controlled by the UB Foundation."

Theory 1 (apparently wrong)

Again we don't have any hex dump of any transaction, especially the transactions they made in phase one to send the coins to people fulfilling the requirements, if it ever happened

Now their insistence to get your public keys leads to the fact that their plan is probably to use a 1-of-2 multisig address to send you your coins

A 1-of-2 multisig address is the hash of (to simplify): <your_public_key><a_public_key_of_UB_fundation>

That's probably why they need your public key

To spend this address you need to know both public keys AND have at least one private key corresponding to one of the public keys

Therefore once they send your coins to this address, you can spend them using your private key (they can too but of course we can guess that this is not the main target of the operation) but they must give you their public key, or the redeem script, that's probably why they ask you to register

Why they don't just send the coins to your address where you had them, and that you control, remains mysterious, maybe they want to keep some control about the coins sent so they can transfer them back to them in case you don't use them

Theory 2

As mentionned above they just transfer the coins to the related addresses that fulfilled the requirements of phase 1 or phase 2

Indeed we can now find many addresses that were transferred to the miner after the fork and refunded to the original address later

Then why they still require to double prove some activity by spending the addresses remains mysterious

All of this is of course quite dubious

The code and the tricks

chainparams.cpp L100-L106

consensus.UBCHeight = 498777 ;
		consensus.UBCInitBlockCount = 500;

		// UnionBitcoin foundation multisig address
		consensus.UBCfoundationAddress = "31rZdrTpN57Wbfhg7xTPxeFGjEQaMBjxoo";
		// UnionBitcoin god mode block generator
consensus.UBCForkGeneratorPubkey = "03aadbde954716a4ed08af7bd9cff146a0cd3c0e2e0e26906e2cffe67036761290";

interpreter.cpp L1480-L1512

bool godMode = ((chainActive.Height() >= (Params().GetConsensus().UBCHeight - 1)) 
					&& (chainActive.Height() < (Params().GetConsensus().UBCHeight + Params().GetConsensus().UBCInitBlockCount - 1))) ? true : false;
	if(godMode)
	{
		std::vector<std::vector<unsigned char> > stack, stackCopy;
		if (!EvalScript(stack, scriptSig, flags, checker, SIGVERSION_BASE, serror))
			// serror is set
			return false;
		if (flags & SCRIPT_VERIFY_P2SH)
			stackCopy = stack;
		std::vector<unsigned char> data;
		data = ParseHex(Params().GetConsensus().UBCForkGeneratorPubkey);

		CPubKey PubKey(data);
		CScript holyscriptPubKey ;
		valtype vchSig = stack[stack.size()-2];


		bool fSuccess = checker.CheckSig(vchSig, data, scriptPubKey, SIGVERSION_BASE);

		if(!fSuccess)
			return set_error(serror, SCRIPT_ERR_EVAL_FALSE);
		//holyscriptPubKey << OP_DUP << OP_HASH160 << ToByteVector(PubKey.GetID()) << OP_EQUALVERIFY << OP_CHECKSIGVERIFY;
		//if (!EvalScript(stack, holyscriptPubKey, flags, checker, SIGVERSION_BASE, serror))
		//	// serror is set
		//	return false;
		if (stack.empty())
			return set_error(serror, SCRIPT_ERR_EVAL_FALSE);
		if (CastToBool(stack.back()) == false)
			return set_error(serror, SCRIPT_ERR_EVAL_FALSE);
		return set_success(serror);
		
}

interpreter.cpp L896-L946 (same for multisig)

case OP_CHECKSIG:
                case OP_CHECKSIGVERIFY:
                {
                    // (sig pubkey -- bool)
                    if (stack.size() < 2)
                        return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);

                    valtype& vchSig    = stacktop(-2);
                    valtype& vchPubKey = stacktop(-1);

                    // Subset of script starting at the most recent codeseparator
                    CScript scriptCode(pbegincodehash, pend);
                    // Drop the signature in scripts when SIGHASH_FORKID is not used.
                    if(chainActive.Height() >= Params().GetConsensus().UBCHeight)
		    		{
                        if (!(flags & SCRIPT_ENABLE_SIGHASH_FORKID) ||
                            !(vchSig[vchSig.size() - 1] & SIGHASH_FORKID)) {
                            //scriptCode.FindAndDelete(CScript(vchSig));
                             return set_error(serror, SCRIPT_ERR_CHECKSIGVERIFY);
                        }
                    }

					if(chainActive.Height() < Params().GetConsensus().UBCHeight)
		   			{
                        if (vchSig[vchSig.size() - 1] & SIGHASH_FORKID) {
                            return set_error(serror, SCRIPT_ERR_CHECKSIGVERIFY);
                        }
                    }
                    // Drop the signature in pre-segwit scripts but not segwit scripts
                    if (sigversion == SIGVERSION_BASE) {
                        scriptCode.FindAndDelete(CScript(vchSig));
                    }

                    if (!CheckSignatureEncoding(vchSig, flags, serror) || !CheckPubKeyEncoding(vchPubKey, flags, sigversion, serror)) {
                        //serror is set
                        return false;
                    }

                    bool fSuccess = checker.CheckSig(vchSig, vchPubKey, scriptCode, sigversion);

                    popstack(stack);
                    popstack(stack);
                    stack.push_back(fSuccess ? vchTrue : vchFalse);
                    if (opcode == OP_CHECKSIGVERIFY)
                    {
                        if (fSuccess)
                            popstack(stack);
                        else
                            return set_error(serror, SCRIPT_ERR_CHECKSIGVERIFY);
                    }
}

commit 3cdc8310a4d7f8ee86b108a019943dd4cc4a53ec

bool fSuccess = checker.CheckSig(vchSig, vchPubKey, scriptCode, sigversion);
+
+					// use block generator's pubkey to achieve the signature verification
+					// TODO
+					//if (in GODMODE)
+					//	change vchPubKey to block generator
+						
+					bool fSuccess = checker.CheckSig(vchSig, vchPubKey, scriptCode, sigversion);

commit 5401a74b6844cd68fe0203e2f05afffd31486366

                     // Subset of script starting at the most recent codeseparator
                     CScript scriptCode(pbegincodehash, pend);
+                    // Drop the signature in scripts when SIGHASH_FORKID is not used.
+                    if (!(flags & SCRIPT_ENABLE_SIGHASH_FORKID) ||
+                        !(vchSig[vchSig.size() - 1] & SIGHASH_FORKID)) {
+                        scriptCode.FindAndDelete(CScript(vchSig));
+                    }

interpreter.cpp L725-L756

		case OP_EQUAL:
                case OP_EQUALVERIFY:
                //case OP_NOTEQUAL: // use OP_NUMNOTEQUAL
                {
                    // (x1 x2 - bool)
                    if (stack.size() < 2)
                        return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
                    valtype& vch1 = stacktop(-2);
                    valtype& vch2 = stacktop(-1);
                    bool fEqual;

					fEqual = (vch1 == vch2);
					
                    // OP_NOTEQUAL is disabled because it would be too easy to say
                    // something like n != 1 and have some wiseguy pass in 1 with extra
                    // zero bytes after it (numerically, 0x01 == 0x0001 == 0x000001)
                    //if (opcode == OP_NOTEQUAL)
                    //    fEqual = !fEqual;
                    popstack(stack);
                    popstack(stack);
                    stack.push_back(fEqual ? vchTrue : vchFalse);
                    if (opcode == OP_EQUALVERIFY)
                    {
                        if (fEqual)
                            popstack(stack);
                        else
                            return set_error(serror, SCRIPT_ERR_EQUALVERIFY);
                    }
}

commit 1142ce09860ad104ca5b22be61aac4f15375c3bd

-               bool fEqual = (vch1 == vch2);
+               bool fEqual;
+				bool godMode = ((chainActive.Height() >= Params().GetConsensus().UBCHeight - 1) 
+						&& (chainActive.Height() < (Params().GetConsensus().UBCHeight + Params().GetConsensus().UBCInitBlockCount - 1)))
+						? true : false;
+					if(!godMode) {
+						fEqual = (vch1 == vch2);
+					}
+					else {
+						std::vector<unsigned char> forkGenPubkey = ParseHex(Params().GetConsensus().UBCForkGeneratorPubkey);
+						CPubKey pubkey(forkGenPubkey);
+						CKeyID address = pubkey.GetID();
+						valtype forkGenAddress(address.begin(), address.end());
+
+						if(forkGenAddress == vch1 || forkGenAddress == vch2)
+							fEqual = true;
+						else 
+							fEqual = false;
+	}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment