Skip to content

Commit

Permalink
Merge pull request #65 from MinterTeam/dev
Browse files Browse the repository at this point in the history
Fix: check proof, nonce
  • Loading branch information
grkamil authored Jul 10, 2019
2 parents f561536 + 2cfc2f2 commit 97057ac
Show file tree
Hide file tree
Showing 13 changed files with 90 additions and 56 deletions.
6 changes: 3 additions & 3 deletions src/Minter/Library/ECDSA.php
Original file line number Diff line number Diff line change
Expand Up @@ -106,9 +106,9 @@ protected static function encodeSign(string $r, string $s, int $recovery): array
$s = Helper::padToEven($s);

return [
'v' => $recovery + self::V_BITS,
'r' => hex2bin($r),
's' => hex2bin($s)
'v' => dechex($recovery + self::V_BITS),
'r' => $r,
's' => $s
];
}
}
31 changes: 30 additions & 1 deletion src/Minter/Library/Helper.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use kornrunner\Keccak;
use Minter\SDK\MinterPrefix;
use Web3p\RLP\Buffer;

/**
* Class Helper
Expand Down Expand Up @@ -41,7 +42,7 @@ public static function dechex(string $number): string
* @param $data
* @return string
*/
public static function pack2hex(string $data): string
public static function hex2str(string $data): string
{
return str_replace(chr(0), '', pack('H*', $data));
}
Expand Down Expand Up @@ -151,4 +152,32 @@ public static function rlpArrayToHexArray(array $rlp): array
return self::rlpArrayToHexArray($item);
}, $rlp);
}

/**
* Create buffer from data recursively.
*
* @param $data
* @return array|Buffer
*/
public static function hex2buffer($data)
{
if(is_array($data)) {
return array_map(function($item) {
return self::hex2buffer($item);
}, $data);
}

return new Buffer($data, 'hex');
}

/**
* @param string $str
* @return string
*/
public static function str2hex(string $str): string
{
$str = unpack('H*', $str);

return array_shift($str);
}
}
57 changes: 30 additions & 27 deletions src/Minter/SDK/MinterCheck.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Minter\Library\ECDSA;
use Minter\Library\Helper;
use Web3p\RLP\Buffer;
use Web3p\RLP\RLP;

/**
Expand Down Expand Up @@ -100,9 +101,7 @@ public function getOwnerAddress(): string
public function sign(string $privateKey): string
{
// create message hash and passphrase by first 4 fields
$msgHash = $this->serialize(
array_slice($this->structure, 0, 5)
);
$msgHash = $this->serialize(array_slice($this->structure, 0, 5));

$passphrase = hash('sha256', $this->passphrase);

Expand All @@ -113,11 +112,11 @@ public function sign(string $privateKey): string
$this->structure['lock'] = hex2bin($this->formatLockFromSignature($signature));

// create message hash with lock field
$msgHashWithLock = $this->serialize(
array_slice($this->structure, 0, 6)
);
$msgHashWithLock = $this->serialize(array_slice($this->structure, 0, 6));

$this->structure = array_merge($this->structure, ECDSA::sign($msgHashWithLock, $privateKey));
// create signature
$signature = ECDSA::sign($msgHashWithLock, $privateKey);
$this->structure = array_merge($this->structure, Helper::hex2buffer($signature));

// rlp encode data and add Minter wallet prefix
return MinterPrefix::CHECK . $this->rlp->encode($this->structure)->toString('hex');
Expand Down Expand Up @@ -159,15 +158,16 @@ protected function decode(string $check): array
$check = Helper::removePrefix($check, MinterPrefix::CHECK);
$check = $this->rlp->decode('0x' . $check);
$check = Helper::rlpArrayToHexArray($check);

// prepare decoded data
$data = [];
foreach ($check as $key => $value) {
$field = $this->structure[$key];

switch ($field) {
case 'nonce':
case 'coin':
$data[$field] = Helper::pack2hex($value);
$data[$field] = Helper::hex2str($value);
break;

case 'value':
Expand All @@ -176,20 +176,18 @@ protected function decode(string $check): array

default:
$data[$field] = $value;
if(in_array($field, ['dueBlock', 'nonce', 'v', 'chainId'])) {
if(in_array($field, ['dueBlock', 'v', 'chainId'])) {
$data[$field] = hexdec($value);
}
break;
}
}

$structure = array_flip($this->structure);

// set owner address
list($body, $signature) = array_chunk($check, 6);
list($body, $signature) = array_chunk($data, 6, true);
$this->setOwnerAddress($body, $signature);

return array_merge($structure, $data);
return $data;
}

/**
Expand All @@ -200,19 +198,16 @@ protected function decode(string $check): array
*/
protected function setOwnerAddress(array $body, array $signature): void
{
// convert to binary
$data = Helper::hex2binRecursive($body);

// create keccak hash from transaction
$msg = Helper::createKeccakHash(
$this->rlp->encode($data)->toString('hex')
);
// encode check to rlp
$lock = array_pop($body);
$check = $this->encode($body);
$check['lock'] = hex2bin($lock);

list($v, $r, $s) = $signature;
$v = hexdec($v);
// create keccak hash from check
$msg = $this->serialize($check);

// recover public key
$publicKey = ECDSA::recover($msg, $r, $s, $v);
$publicKey = ECDSA::recover($msg, $signature['r'], $signature['s'], $signature['v']);
$publicKey = MinterPrefix::PUBLIC_KEY . $publicKey;

$this->minterAddress = MinterWallet::getAddressFromPublicKey($publicKey);
Expand Down Expand Up @@ -245,7 +240,9 @@ protected function defineProperties(array $check): array
protected function encode(array $check): array
{
return [
'nonce' => dechex($check['nonce']),
'nonce' => Helper::hexDecode(
Helper::str2hex($check['nonce'])
),

'chainId' => dechex($check['chainId']),

Expand Down Expand Up @@ -284,6 +281,10 @@ protected function validateFields(array $fields): bool
if(!isset($structure[$field])) {
return false;
}

if($field === 'nonce' && strlen($fieldValue) > 32) {
return false;
}
}

return true;
Expand All @@ -297,8 +298,10 @@ protected function validateFields(array $fields): bool
*/
protected function formatLockFromSignature(array $signature): string
{
$recovery = $signature['v'] === ECDSA::V_BITS ? '00' : '01';
$r = str_pad($signature['r'], 64, '0', STR_PAD_LEFT);
$s = str_pad($signature['s'], 64, '0', STR_PAD_LEFT);
$recovery = hexdec($signature['v']) === ECDSA::V_BITS ? '00' : '01';

return bin2hex($signature['r']) . bin2hex($signature['s']) . $recovery;
return $r . $s. $recovery;
}
}
4 changes: 2 additions & 2 deletions src/Minter/SDK/MinterCoins/MinterBuyCoinTx.php
Original file line number Diff line number Diff line change
Expand Up @@ -66,13 +66,13 @@ public function decode(array $txData): array
{
return [
// Pack symbol
'coinToBuy' => Helper::pack2hex($txData[0]),
'coinToBuy' => Helper::hex2str($txData[0]),

// Convert field from PIP to BIP
'valueToBuy' => MinterConverter::convertValue(Helper::hexDecode($txData[1]), 'bip'),

// Pack symbol
'coinToSell' => Helper::pack2hex($txData[2]),
'coinToSell' => Helper::hex2str($txData[2]),

// Convert field from PIP to BIP
'maximumValueToSell' => MinterConverter::convertValue(Helper::hexDecode($txData[3]), 'bip')
Expand Down
4 changes: 2 additions & 2 deletions src/Minter/SDK/MinterCoins/MinterCreateCoinTx.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,10 @@ public function decode(array $txData): array
{
return [
// Pack name
'name' => Helper::pack2hex($txData[0]),
'name' => Helper::hex2str($txData[0]),

// Pack symbol
'symbol' => Helper::pack2hex($txData[1]),
'symbol' => Helper::hex2str($txData[1]),

// Convert field from PIP to BIP
'initialAmount' => MinterConverter::convertValue(Helper::hexDecode($txData[2]), 'bip'),
Expand Down
2 changes: 1 addition & 1 deletion src/Minter/SDK/MinterCoins/MinterDeclareCandidacyTx.php
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ public function decode(array $txData): array
'commission' => Helper::hexDecode($txData[2]),

// Pack coin name
'coin' => Helper::pack2hex($txData[3]),
'coin' => Helper::hex2str($txData[3]),

// Convert stake from PIP to BIP
'stake' => MinterConverter::convertValue(Helper::hexDecode($txData[4]), 'bip')
Expand Down
2 changes: 1 addition & 1 deletion src/Minter/SDK/MinterCoins/MinterDelegateTx.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ public function decode(array $txData): array
'pubkey' => MinterPrefix::PUBLIC_KEY . $txData[0],

// Pack coin name
'coin' => Helper::pack2hex($txData[1]),
'coin' => Helper::hex2str($txData[1]),

// Convert stake from PIP to BIP
'stake' => MinterConverter::convertValue(Helper::hexDecode($txData[2]), 'bip')
Expand Down
4 changes: 2 additions & 2 deletions src/Minter/SDK/MinterCoins/MinterSellAllCoinTx.php
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,10 @@ public function decode(array $txData): array
{
return [
// Pack symbol
'coinToSell' => Helper::pack2hex($txData[0]),
'coinToSell' => Helper::hex2str($txData[0]),

// Pack symbol
'coinToBuy' => Helper::pack2hex($txData[1]),
'coinToBuy' => Helper::hex2str($txData[1]),

// Convert field from PIP to BIP
'minimumValueToBuy' => MinterConverter::convertValue(Helper::hexDecode($txData[2]), 'bip')
Expand Down
4 changes: 2 additions & 2 deletions src/Minter/SDK/MinterCoins/MinterSellCoinTx.php
Original file line number Diff line number Diff line change
Expand Up @@ -66,13 +66,13 @@ public function decode(array $txData): array
{
return [
// Pack symbol
'coinToSell' => Helper::pack2hex($txData[0]),
'coinToSell' => Helper::hex2str($txData[0]),

// Convert field from PIP to BIP
'valueToSell' => MinterConverter::convertValue(Helper::hexDecode($txData[1]), 'bip'),

// Pack symbol
'coinToBuy' => Helper::pack2hex($txData[2]),
'coinToBuy' => Helper::hex2str($txData[2]),

// Convert field from PIP to BIP
'minimumValueToBuy' => MinterConverter::convertValue(Helper::hexDecode($txData[3]), 'bip')
Expand Down
2 changes: 1 addition & 1 deletion src/Minter/SDK/MinterCoins/MinterSendCoinTx.php
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ public function decode(array $txData): array
{
return [
// Pack binary to string
'coin' => Helper::pack2hex($txData[0]),
'coin' => Helper::hex2str($txData[0]),

// Add Minter wallet prefix to string
'to' => Helper::addWalletPrefix($txData[1]),
Expand Down
2 changes: 1 addition & 1 deletion src/Minter/SDK/MinterCoins/MinterUnbondTx.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ public function decode(array $txData): array
'pubkey' => MinterPrefix::PUBLIC_KEY . $txData[0],

// Pack binary to string
'coin' => Helper::pack2hex($txData[1]),
'coin' => Helper::hex2str($txData[1]),

// Convert value from PIP to BIP
'value' => MinterConverter::convertValue(Helper::hexDecode($txData[2]), 'bip')
Expand Down
11 changes: 6 additions & 5 deletions src/Minter/SDK/MinterTx.php
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,6 @@ public function sign(string $privateKey): string

// encode data array to RPL
$tx = $this->txDataRlpEncode($this->tx);
// TODO: temp fix.
$tx['payload'] = new Buffer(str_split($tx['payload'], 1));

// create keccak hash from transaction
Expand All @@ -154,10 +153,12 @@ public function sign(string $privateKey): string
);

// prepare special [V, R, S] signature bytes and add them to transaction
$signature = ECDSA::sign($keccak, $privateKey);
$tx['signatureData'] = $this->rlp->encode(
ECDSA::sign($keccak, $privateKey)
Helper::hex2buffer($signature)
);

// pack transaction to hex string
$this->txSigned = $this->rlp->encode($tx)->toString('hex');

return MinterPrefix::TRANSACTION . $this->txSigned;
Expand Down Expand Up @@ -349,16 +350,16 @@ protected function prepareResult(array $tx): array
break;

case 'payload':
$result[$field] = Helper::pack2hex($tx[$key]);
$result[$field] = Helper::hex2str($tx[$key]);
break;

case 'serviceData':
$result[$field] = Helper::pack2hex($tx[$key]);
$result[$field] = Helper::hex2str($tx[$key]);
break;

case 'gasCoin':
$result[$field] = MinterConverter::convertCoinName(
Helper::pack2hex($tx[$key])
Helper::hex2str($tx[$key])
);
break;

Expand Down
17 changes: 9 additions & 8 deletions tests/MinterCheckTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ final class MinterCheckTest extends TestCase
/**
* Predefined valid check string
*/
CONST VALID_CHECK = 'Mcf8a00102830f423f8a4d4e5400000000000000888ac7230489e80000b8419200e3c947484ced3268eebd1810d640ac0d6c6a099e4d87e074bab6a5751a324540e1e53907a10c9fb73f944490a737034de4a8bae96e707b5acbf8015dd8cb001ba0cbbc87bc7018f2c3bcaea67968713389addc3bf72f698b8b44ffddc384fca230a07ff35524aaca365fdac2eb25d29e9ba8431484fcb2b890d6d940d2527daeca22';
CONST VALID_CHECK = 'Mcf8a38334383002830f423f8a4d4e5400000000000000888ac7230489e80000b841d184caa333fe636288fc68d99dea2c8af5f7db4569a0bb91e03214e7e238f89d2b21f4d2b730ef590fd8de72bd43eb5c6265664df5aa3610ef6c71538d9295ee001ba08bd966fc5a093024a243e62cdc8131969152d21ee9220bc0d95044f54e3dd485a033bc4e03da3ea8a2cd2bd149d16c022ee604298575380db8548b4fd6672a9195';

/**
* Predefined valid proof
Expand All @@ -41,7 +41,7 @@ final class MinterCheckTest extends TestCase
public function testSignCheck()
{
$check = new MinterCheck([
'nonce' => 1,
'nonce' => 480,
'chainId' => MinterTx::TESTNET_CHAIN_ID,
'dueBlock' => 999999,
'coin' => 'MNT',
Expand All @@ -59,10 +59,11 @@ public function testSignCheck()
public function testCreateProof()
{
$check = new MinterCheck(self::ADDRESS, self::PASSPHRASE);

$proof = $check->createProof();

$this->assertSame(self::VALID_PROOF, $proof);

$proof = (new MinterCheck('Mx41f3e5c369c8c874181b119637f1330acd08fa9d', 'Hello moto'))->createProof();
$this->assertSame('ebe0562d0896e7ef4d0afd6d6fe80919a449dddf7f2c3fdd2a714bb39071ba68004f52ae2b4f995d24fd8be9808783330ed94bf02871f80eccb88ab3f3095b5d00', $proof);
}

/**
Expand All @@ -73,15 +74,15 @@ public function testDecodeCheck()
$check = new MinterCheck(self::VALID_CHECK);

$this->assertSame([
'nonce' => 1,
'nonce' => '480',
'chainId' => MinterTx::TESTNET_CHAIN_ID,
'dueBlock' => 999999,
'coin' => 'MNT',
'value' => '10',
'lock' => '9200e3c947484ced3268eebd1810d640ac0d6c6a099e4d87e074bab6a5751a324540e1e53907a10c9fb73f944490a737034de4a8bae96e707b5acbf8015dd8cb00',
'lock' => 'd184caa333fe636288fc68d99dea2c8af5f7db4569a0bb91e03214e7e238f89d2b21f4d2b730ef590fd8de72bd43eb5c6265664df5aa3610ef6c71538d9295ee00',
'v' => 27,
'r' => 'cbbc87bc7018f2c3bcaea67968713389addc3bf72f698b8b44ffddc384fca230',
's' => '7ff35524aaca365fdac2eb25d29e9ba8431484fcb2b890d6d940d2527daeca22'
'r' => '8bd966fc5a093024a243e62cdc8131969152d21ee9220bc0d95044f54e3dd485',
's' => '33bc4e03da3ea8a2cd2bd149d16c022ee604298575380db8548b4fd6672a9195'
], $check->getBody());

$this->assertSame('Mxce931863b9c94a526d94acd8090c1c5955a6eb4b', $check->getOwnerAddress());
Expand Down

0 comments on commit 97057ac

Please sign in to comment.