From 50079609544089acff3d39776348836c2180f075 Mon Sep 17 00:00:00 2001 From: ZuluCrypto Date: Wed, 28 Mar 2018 18:09:37 -0600 Subject: [PATCH 1/6] Implemented XdrEncoder::unsignedBigInteger64 --- src/Xdr/XdrEncoder.php | 30 ++++++++++++++++++++++++++++++ tests/Unit/Xdr/XdrEncoderTest.php | 18 ++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/src/Xdr/XdrEncoder.php b/src/Xdr/XdrEncoder.php index 2f16a10..edb967b 100755 --- a/src/Xdr/XdrEncoder.php +++ b/src/Xdr/XdrEncoder.php @@ -120,6 +120,36 @@ public static function signedBigInteger64(BigInteger $value) return XdrEncoder::opaqueFixed($xdrBytes, 8); } + /** + * Converts $value to an unsigned 8-byte big endian uint64 + * + * @param BigInteger $value + * @return string + */ + public static function unsignedBigInteger64(BigInteger $value) + { + $xdrBytes = ''; + $bigIntBytes = $value->toBytes(true); + + // Special case: MAX_UINT_64 will look like 00ffffffffffffffff and have an + // extra preceeding byte we need to get rid of + if (strlen($bigIntBytes) === 9 && substr($value->toHex(true), 0, 2) === '00') { + $bigIntBytes = substr($bigIntBytes, 1); + } + + $paddingChar = chr(0); + + $paddingBytes = 8 - strlen($bigIntBytes); + while ($paddingBytes > 0) { + $xdrBytes .= $paddingChar; + $paddingBytes--; + } + + $xdrBytes .= $bigIntBytes; + + return XdrEncoder::opaqueFixed($xdrBytes, 8); + } + /** * Use this to write raw bytes representing a 64-bit integer * diff --git a/tests/Unit/Xdr/XdrEncoderTest.php b/tests/Unit/Xdr/XdrEncoderTest.php index d11ea38..77e648e 100644 --- a/tests/Unit/Xdr/XdrEncoderTest.php +++ b/tests/Unit/Xdr/XdrEncoderTest.php @@ -32,6 +32,24 @@ public function testSignedBigInteger64() $this->assertBytesEqual('80 00 00 00 00 00 00 00', XdrEncoder::signedBigInteger64(new BigInteger("-9223372036854775808"))); } + public function testUnsignedBigInteger64() + { + // 100 + $this->assertBytesEqual('00 00 00 00 00 00 00 64', XdrEncoder::unsignedBigInteger64(new BigInteger("100"))); + + // 18446744073709551615 + $this->assertBytesEqual('ff ff ff ff ff ff ff ff', XdrEncoder::unsignedBigInteger64(new BigInteger("18446744073709551615"))); + + // MAX_INT + $this->assertBytesEqual('00 00 00 00 ff ff ff ff', XdrEncoder::unsignedBigInteger64(new BigInteger("4294967295"))); + + // MAX_INT + 1 + $this->assertBytesEqual('00 00 00 01 00 00 00 00', XdrEncoder::unsignedBigInteger64(new BigInteger("4294967296"))); + + // max positive signed int64 + $this->assertBytesEqual('7f ff ff ff ff ff ff ff', XdrEncoder::unsignedBigInteger64(new BigInteger("9223372036854775807"))); + } + public function testOpaqueVariable() { // Test padding is applied when characters are not a multiple of 4 From b30dad904c4c2aa72dce8c01df1ab7b7f2cc6032 Mon Sep 17 00:00:00 2001 From: ZuluCrypto Date: Sat, 31 Mar 2018 12:50:28 -0600 Subject: [PATCH 2/6] implemented BumpSequenceOp --- src/XdrModel/Operation/BumpSequenceOp.php | 67 +++++++++++++++++++ src/XdrModel/Operation/Operation.php | 4 ++ .../XdrModel/Operation/BumpSequenceOpTest.php | 25 +++++++ 3 files changed, 96 insertions(+) create mode 100644 src/XdrModel/Operation/BumpSequenceOp.php create mode 100644 tests/Unit/XdrModel/Operation/BumpSequenceOpTest.php diff --git a/src/XdrModel/Operation/BumpSequenceOp.php b/src/XdrModel/Operation/BumpSequenceOp.php new file mode 100644 index 0000000..d75d704 --- /dev/null +++ b/src/XdrModel/Operation/BumpSequenceOp.php @@ -0,0 +1,67 @@ +bumpTo = $bumpTo; + } + + /** + * @return string + */ + public function toXdr() + { + $bytes = parent::toXdr(); + + $bytes .= XdrEncoder::unsignedBigInteger64($this->bumpTo); + + return $bytes; + } + + /** + * NOTE: This only parses the XDR that's specific to this operation and cannot + * load a full Operation + * + * @deprecated Do not call this directly, instead call Operation::fromXdr() + * @param XdrBuffer $xdr + * @return BumpSequenceOp + * @throws \ErrorException + */ + public static function fromXdr(XdrBuffer $xdr) + { + return new BumpSequenceOp($xdr->readBigInteger()); + } + + /** + * @return BigInteger + */ + public function getBumpTo() + { + return $this->bumpTo; + } + + /** + * @param BigInteger $bumpTo + */ + public function setBumpTo($bumpTo) + { + $this->bumpTo = $bumpTo; + } +} \ No newline at end of file diff --git a/src/XdrModel/Operation/Operation.php b/src/XdrModel/Operation/Operation.php index 39f2444..c731726 100755 --- a/src/XdrModel/Operation/Operation.php +++ b/src/XdrModel/Operation/Operation.php @@ -43,6 +43,7 @@ abstract class Operation implements XdrEncodableInterface const TYPE_ACCOUNT_MERGE = 8; const TYPE_INFLATION = 9; const TYPE_MANAGE_DATA = 10; + const TYPE_BUMP_SEQUENCE = 11; /** * @var AccountId @@ -151,6 +152,9 @@ public static function fromXdr(XdrBuffer $xdr) case Operation::TYPE_MANAGE_DATA: $model = ManageDataOp::fromXdr($xdr); break; + case Operation::TYPE_BUMP_SEQUENCE: + $model = BumpSequenceOp::fromXdr($xdr); + break; default: throw new \InvalidArgumentException(sprintf('unrecognized operation type %s', $type)); } diff --git a/tests/Unit/XdrModel/Operation/BumpSequenceOpTest.php b/tests/Unit/XdrModel/Operation/BumpSequenceOpTest.php new file mode 100644 index 0000000..25584b6 --- /dev/null +++ b/tests/Unit/XdrModel/Operation/BumpSequenceOpTest.php @@ -0,0 +1,25 @@ +toXdr())); + + $this->assertTrue($parsed instanceof BumpSequenceOp); + $this->assertEquals($source->getBumpTo()->toString(), $parsed->getBumpTo()->toString()); + } +} \ No newline at end of file From e094d48ffcf5daf15694dfa516d5f87d3d80f4b1 Mon Sep 17 00:00:00 2001 From: ZuluCrypto Date: Tue, 3 Apr 2018 19:59:53 -0600 Subject: [PATCH 3/6] TrezorSigner - update to use new arguments to trezorctl --- src/Signing/TrezorSigner.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Signing/TrezorSigner.php b/src/Signing/TrezorSigner.php index 70cd4b3..8f2c896 100644 --- a/src/Signing/TrezorSigner.php +++ b/src/Signing/TrezorSigner.php @@ -52,9 +52,11 @@ public function signTransaction(TransactionBuilder $builder) $networkPassphrase = $builder->getApiClient()->getNetworkPassphrase(); - $cmd = sprintf('%s stellar_sign_transaction -a %s -n %s %s', + $bip32Path = sprintf("m/44'/148'/%s'", $this->accountIndex); + + $cmd = sprintf('%s stellar_sign_transaction --address %s -n %s %s', $this->trezorBinPath, - escapeshellarg(($this->accountIndex + 1)), + escapeshellarg($bip32Path), escapeshellarg($networkPassphrase), escapeshellarg(base64_encode($xdr)) ); From 4b3ce4a022202b6307b90d2ed2a4f0c4e90cc086 Mon Sep 17 00:00:00 2001 From: ZuluCrypto Date: Tue, 3 Apr 2018 20:00:52 -0600 Subject: [PATCH 4/6] HardwareWalletIntegrationTest - use the same mnemonic the Trezor unit tests do --- tests/Util/HardwareWalletIntegrationTest.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tests/Util/HardwareWalletIntegrationTest.php b/tests/Util/HardwareWalletIntegrationTest.php index 8620035..e04c439 100644 --- a/tests/Util/HardwareWalletIntegrationTest.php +++ b/tests/Util/HardwareWalletIntegrationTest.php @@ -45,7 +45,14 @@ public function __construct($name = null, array $data = [], $dataName = '') // GDRXE2BQUC3AZNPVFSCEZ76NJ3WWL25FYFK6RGZGIEKWE4SOOHSUJUJ6 // SBGWSG6BTNCKCOB3DIFBGCVMUPQFYPA2G4O34RMTB343OYPXU5DJDVMN - $this->mnemonic = 'illness spike retreat truth genius clock brain pass fit cave bargain toe'; + //$this->mnemonic = 'illness spike retreat truth genius clock brain pass fit cave bargain toe'; + + // Mnemonic to match trezor-python test suite + // GAK5MSF74TJW6GLM7NLTL76YZJKM2S4CGP3UH4REJHPHZ4YBZW2GSBPW + // SDE2YU4V2IYSJIUH7MONDYZTSSLDXV5QDEGUUOLCU4TK7CZWTAXZ5CEG + $this->mnemonic = 'alcohol woman abuse must during monitor noble actual mixed trade anger aisle'; + + $this->privateKeySigner = new PrivateKeySigner(Keypair::newFromMnemonic($this->mnemonic)); } From 91882a4f7fa7338a6880ad30022bfe3ac0abc416 Mon Sep 17 00:00:00 2001 From: ZuluCrypto Date: Tue, 3 Apr 2018 20:01:17 -0600 Subject: [PATCH 5/6] Implemented TransactionBuilder::bumpSequenceTo --- src/Transaction/TransactionBuilder.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/Transaction/TransactionBuilder.php b/src/Transaction/TransactionBuilder.php index 8ca5ce1..66cb163 100755 --- a/src/Transaction/TransactionBuilder.php +++ b/src/Transaction/TransactionBuilder.php @@ -24,6 +24,7 @@ use ZuluCrypto\StellarSdk\XdrModel\Memo; use ZuluCrypto\StellarSdk\XdrModel\Operation\AccountMergeOp; use ZuluCrypto\StellarSdk\XdrModel\Operation\AllowTrustOp; +use ZuluCrypto\StellarSdk\XdrModel\Operation\BumpSequenceOp; use ZuluCrypto\StellarSdk\XdrModel\Operation\ChangeTrustOp; use ZuluCrypto\StellarSdk\XdrModel\Operation\CreateAccountOp; use ZuluCrypto\StellarSdk\XdrModel\Operation\ManageDataOp; @@ -362,6 +363,16 @@ public function clearAccountData($key, $sourceAccountId = null) return $this->addOperation(new ManageDataOp($key, null, $sourceAccountId)); } + /** + * @param BigInteger $bumpTo + * @param null $sourceAccountId + * @return TransactionBuilder + */ + public function bumpSequenceTo(BigInteger $bumpTo, $sourceAccountId = null) + { + return $this->addOperation(new BumpSequenceOp($bumpTo, $sourceAccountId)); + } + /** * @return string */ From 663a282756867dabb52e9efd18e6ccf083fa7473 Mon Sep 17 00:00:00 2001 From: ZuluCrypto Date: Tue, 3 Apr 2018 20:01:29 -0600 Subject: [PATCH 6/6] Implemented BumpSequenceOpTest hardware wallet test --- tests/HardwareWallet/BumpSequenceOpTest.php | 64 +++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 tests/HardwareWallet/BumpSequenceOpTest.php diff --git a/tests/HardwareWallet/BumpSequenceOpTest.php b/tests/HardwareWallet/BumpSequenceOpTest.php new file mode 100644 index 0000000..eaab8cb --- /dev/null +++ b/tests/HardwareWallet/BumpSequenceOpTest.php @@ -0,0 +1,64 @@ +mnemonic); + + $bumpTo = new BigInteger(1234567890); + + $transaction = $this->horizonServer + ->buildTransaction($sourceKeypair) + ->setSequenceNumber(new BigInteger(4294967296)) + ->bumpSequenceTo($bumpTo); + + $knownSignature = $transaction->signWith($this->privateKeySigner); + + $this->manualVerificationOutput(join(PHP_EOL, [ + ' Bump Sequence: basic', + ' Source: ' . $sourceKeypair->getPublicKey(), + ' Bump To: ' . $bumpTo->toString(), + ])); + $hardwareSignature = $transaction->signWith($this->horizonServer->getSigningProvider()); + + $this->assertEquals($knownSignature->toBase64(), $hardwareSignature->toBase64()); + } + + /** + * @group requires-hardwarewallet + */ + public function testMaxBumpSequence() + { + $sourceKeypair = Keypair::newFromMnemonic($this->mnemonic); + + $bumpTo = new BigInteger('9223372036854775807'); + + $transaction = $this->horizonServer + ->buildTransaction($sourceKeypair) + ->setSequenceNumber(new BigInteger(4294967296)) + ->bumpSequenceTo($bumpTo); + + $knownSignature = $transaction->signWith($this->privateKeySigner); + + $this->manualVerificationOutput(join(PHP_EOL, [ + ' Bump Sequence: max', + ' Source: ' . $sourceKeypair->getPublicKey(), + ' Bump To: ' . $bumpTo->toString(), + ])); + $hardwareSignature = $transaction->signWith($this->horizonServer->getSigningProvider()); + + $this->assertEquals($knownSignature->toBase64(), $hardwareSignature->toBase64()); + } +} \ No newline at end of file