From 0d6e08cbeccf33c429644df8ee86e8326a97d2a2 Mon Sep 17 00:00:00 2001 From: subtly Date: Fri, 13 Feb 2015 02:48:33 -0500 Subject: [PATCH 001/100] initiator handshake crypto-test --- libdevcrypto/ECDHE.h | 2 ++ test/crypto.cpp | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/libdevcrypto/ECDHE.h b/libdevcrypto/ECDHE.h index 4450aec4b..2a09e8e78 100644 --- a/libdevcrypto/ECDHE.h +++ b/libdevcrypto/ECDHE.h @@ -62,6 +62,8 @@ public: /// Public key sent to remote. Public pubkey() { return m_ephemeral.pub(); } + Secret seckey() { return m_ephemeral.sec(); } + /// Input public key for dh agreement, output generated shared secret. void agree(Public const& _remoteEphemeral, Secret& o_sharedSecret); diff --git a/test/crypto.cpp b/test/crypto.cpp index 291893f59..b63bdf596 100644 --- a/test/crypto.cpp +++ b/test/crypto.cpp @@ -316,6 +316,43 @@ BOOST_AUTO_TEST_CASE(ecdhe) BOOST_REQUIRE_EQUAL(sremote, slocal); } +BOOST_AUTO_TEST_CASE(handshakeNew) +{ + // authInitiator -> E(remote-pubk, S(ecdhe-random, ecdh-shared-secret^nonce) || H(ecdhe-random-pubk) || pubk || nonce || 0x0) + // authRecipient -> E(remote-pubk, ecdhe-random-pubk || nonce || 0x0) + + Secret nodeAsecret(sha3("privacy")); + KeyPair nodeA(nodeAsecret); + + Secret nodeBsecret(sha3("privacy++")); + KeyPair nodeB(nodeBsecret); + + // Initiator is Alice (nodeA) + ECDHE eA; + bytes nAbytes(fromHex("0xAAAA")); + h256 nonceA(sha3(nAbytes)); + + bytes auth(Signature::size + h256::size + Public::size + h256::size + 1); + { + bytesConstRef sig(&auth[0], Signature::size); + bytesConstRef hepubk(&auth[Signature::size], h256::size); + bytesConstRef pubk(&auth[Signature::size + h256::size], Public::size); + bytesConstRef nonce(&auth[Signature::size + h256::size + Public::size], h256::size); + + Secret ss; + s_secp256k1.agree(nodeA.sec(), nodeB.pub(), ss); + sign(eA.seckey(), ss ^ nonceA).ref().copyTo(sig); + sha3(eA.pubkey().ref(), hepubk); + nodeA.pub().ref().copyTo(pubk); + nonceA.ref().copyTo(nonce); + auth[auth.size() - 1] = 0x0; + } + + cnote << "initAuth:" << toHex(auth); + +// bytes ack(h256::size * 2 + 1); +} + BOOST_AUTO_TEST_CASE(ecdhe_aes128_ctr_sha3mac) { // New connections require new ECDH keypairs From c25f287d2e54c78b3a547c8f2591748dc97cd3fb Mon Sep 17 00:00:00 2001 From: subtly Date: Fri, 13 Feb 2015 02:57:26 -0500 Subject: [PATCH 002/100] ack handshake for new node and encrypt both sides --- test/crypto.cpp | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/test/crypto.cpp b/test/crypto.cpp index b63bdf596..ed35c3408 100644 --- a/test/crypto.cpp +++ b/test/crypto.cpp @@ -347,10 +347,25 @@ BOOST_AUTO_TEST_CASE(handshakeNew) nonceA.ref().copyTo(nonce); auth[auth.size() - 1] = 0x0; } - - cnote << "initAuth:" << toHex(auth); - -// bytes ack(h256::size * 2 + 1); + bytes authcipher; + encrypt(nodeB.pub(), &auth, authcipher); + cnote << "initAuth:" << toHex(authcipher); + + ECDHE eB; + bytes nBbytes(fromHex("0xBBBB")); + h256 nonceB(sha3(nAbytes)); + bytes ack(h256::size * 2 + 1); + { + bytesConstRef epubk(&auth[0], Secret::size); + bytesConstRef nonce(&auth[Secret::size], h256::size); + + eB.pubkey().ref().copyTo(epubk); + nonceB.ref().copyTo(nonce); + auth[auth.size() - 1] = 0x0; + } + bytes ackcipher; + encrypt(nodeA.pub(), &ack, ackcipher); + cnote << "ackAuth:" << toHex(ackcipher); } BOOST_AUTO_TEST_CASE(ecdhe_aes128_ctr_sha3mac) From 2aad7d856e663798603fb9455d4748bd62866698 Mon Sep 17 00:00:00 2001 From: subtly Date: Fri, 13 Feb 2015 16:05:20 -0500 Subject: [PATCH 003/100] key agreement --- test/crypto.cpp | 45 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 41 insertions(+), 4 deletions(-) diff --git a/test/crypto.cpp b/test/crypto.cpp index ed35c3408..588fb4d6b 100644 --- a/test/crypto.cpp +++ b/test/crypto.cpp @@ -331,17 +331,16 @@ BOOST_AUTO_TEST_CASE(handshakeNew) ECDHE eA; bytes nAbytes(fromHex("0xAAAA")); h256 nonceA(sha3(nAbytes)); - bytes auth(Signature::size + h256::size + Public::size + h256::size + 1); + Secret ssA; { bytesConstRef sig(&auth[0], Signature::size); bytesConstRef hepubk(&auth[Signature::size], h256::size); bytesConstRef pubk(&auth[Signature::size + h256::size], Public::size); bytesConstRef nonce(&auth[Signature::size + h256::size + Public::size], h256::size); - Secret ss; - s_secp256k1.agree(nodeA.sec(), nodeB.pub(), ss); - sign(eA.seckey(), ss ^ nonceA).ref().copyTo(sig); + s_secp256k1.agree(nodeA.sec(), nodeB.pub(), ssA); + sign(eA.seckey(), ssA ^ nonceA).ref().copyTo(sig); sha3(eA.pubkey().ref(), hepubk); nodeA.pub().ref().copyTo(pubk); nonceA.ref().copyTo(nonce); @@ -366,6 +365,44 @@ BOOST_AUTO_TEST_CASE(handshakeNew) bytes ackcipher; encrypt(nodeA.pub(), &ack, ackcipher); cnote << "ackAuth:" << toHex(ackcipher); + + /// Alice + // TODO: decrypt ackcipher and check decrypted pubk is eb + Secret aEncryptK; + Secret aMacK; + Secret aEgressMac; + Secret aIngressMac; + { + bytes keyMaterial(512); + + h256 ess; + // todo: ecdh-agree should be able to output bytes + s_secp256k1.agree(eA.seckey(), eB.pubkey(), ess); + ess.ref().copyTo(bytesConstRef(&keyMaterial).cropped(0, h256::size)); + ssA.ref().copyTo(bytesConstRef(&keyMaterial).cropped(h256::size, h256::size)); +// auto token = sha3(ssA); + aEncryptK = sha3(keyMaterial); + aEncryptK.ref().copyTo(bytesConstRef(&keyMaterial).cropped(h256::size, h256::size)); + aMacK = sha3(keyMaterial); + + // todo: resize key-material and copy ciphertext + aEgressMac = sha3(keyMaterial); + aIngressMac = sha3(keyMaterial); + } + + +// ecdhe-shared-secret = ecdh.agree(ecdhe-random, remote-ecdhe-random-pubk) +// shared-secret = sha3(ecdhe-shared-secret || sha3(nonce || initiator-nonce)) +// token = sha3(shared-secret) +// aes-secret = sha3(ecdhe-shared-secret || shared-secret) +//# destroy shared-secret +// mac-secret = sha3(ecdhe-shared-secret || aes-secret) +//# destroy ecdhe-shared-secret +// egress-mac = sha3(mac-secret^nonce || auth) +//# destroy nonce +// ingress-mac = sha3(mac-secret^initiator-nonce || auth) +//# destroy remote-nonce + } BOOST_AUTO_TEST_CASE(ecdhe_aes128_ctr_sha3mac) From 685a7d7a10904a4cb743360b144fb83f29c673d5 Mon Sep 17 00:00:00 2001 From: subtly Date: Fri, 13 Feb 2015 20:45:03 -0500 Subject: [PATCH 004/100] end-to-end handshake for uknown node --- test/crypto.cpp | 85 ++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 66 insertions(+), 19 deletions(-) diff --git a/test/crypto.cpp b/test/crypto.cpp index 588fb4d6b..646202168 100644 --- a/test/crypto.cpp +++ b/test/crypto.cpp @@ -323,9 +323,13 @@ BOOST_AUTO_TEST_CASE(handshakeNew) Secret nodeAsecret(sha3("privacy")); KeyPair nodeA(nodeAsecret); + BOOST_REQUIRE(nodeA.pub()); Secret nodeBsecret(sha3("privacy++")); KeyPair nodeB(nodeBsecret); + BOOST_REQUIRE(nodeA.pub()); + + BOOST_REQUIRE_NE(nodeA.sec(), nodeB.sec()); // Initiator is Alice (nodeA) ECDHE eA; @@ -348,7 +352,6 @@ BOOST_AUTO_TEST_CASE(handshakeNew) } bytes authcipher; encrypt(nodeB.pub(), &auth, authcipher); - cnote << "initAuth:" << toHex(authcipher); ECDHE eB; bytes nBbytes(fromHex("0xBBBB")); @@ -364,45 +367,89 @@ BOOST_AUTO_TEST_CASE(handshakeNew) } bytes ackcipher; encrypt(nodeA.pub(), &ack, ackcipher); - cnote << "ackAuth:" << toHex(ackcipher); + + BOOST_REQUIRE(eA.pubkey()); + BOOST_REQUIRE(eB.pubkey()); + BOOST_REQUIRE_NE(eA.seckey(), eB.seckey()); /// Alice - // TODO: decrypt ackcipher and check decrypted pubk is eb Secret aEncryptK; Secret aMacK; Secret aEgressMac; Secret aIngressMac; { - bytes keyMaterial(512); + // TODO: decrypt ackcipher and check decrypted material + // TODO: export ess and require equal to b + + bytes keyMaterialBytes(512); + bytesConstRef keyMaterial(&keyMaterialBytes); h256 ess; // todo: ecdh-agree should be able to output bytes s_secp256k1.agree(eA.seckey(), eB.pubkey(), ess); - ess.ref().copyTo(bytesConstRef(&keyMaterial).cropped(0, h256::size)); - ssA.ref().copyTo(bytesConstRef(&keyMaterial).cropped(h256::size, h256::size)); + ess.ref().copyTo(keyMaterial.cropped(0, h256::size)); + ssA.ref().copyTo(keyMaterial.cropped(h256::size, h256::size)); // auto token = sha3(ssA); aEncryptK = sha3(keyMaterial); - aEncryptK.ref().copyTo(bytesConstRef(&keyMaterial).cropped(h256::size, h256::size)); + aEncryptK.ref().copyTo(keyMaterial.cropped(h256::size, h256::size)); aMacK = sha3(keyMaterial); - // todo: resize key-material and copy ciphertext + // todo: replace nonceB with decrypted nonceB + keyMaterialBytes.resize(h256::size + authcipher.size()); + keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); + (aMacK ^ nonceB).ref().copyTo(keyMaterial); + bytesConstRef(&authcipher).copyTo(keyMaterial.cropped(h256::size, authcipher.size())); aEgressMac = sha3(keyMaterial); + + keyMaterialBytes.resize(h256::size + ackcipher.size()); + keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); + (aMacK ^ nonceA).ref().copyTo(keyMaterial); + bytesConstRef(&ackcipher).copyTo(keyMaterial.cropped(h256::size, ackcipher.size())); aIngressMac = sha3(keyMaterial); } -// ecdhe-shared-secret = ecdh.agree(ecdhe-random, remote-ecdhe-random-pubk) -// shared-secret = sha3(ecdhe-shared-secret || sha3(nonce || initiator-nonce)) -// token = sha3(shared-secret) -// aes-secret = sha3(ecdhe-shared-secret || shared-secret) -//# destroy shared-secret -// mac-secret = sha3(ecdhe-shared-secret || aes-secret) -//# destroy ecdhe-shared-secret -// egress-mac = sha3(mac-secret^nonce || auth) -//# destroy nonce -// ingress-mac = sha3(mac-secret^initiator-nonce || auth) -//# destroy remote-nonce + /// Bob + Secret ssB; + s_secp256k1.agree(nodeB.sec(), nodeA.pub(), ssB); + BOOST_REQUIRE_EQUAL(ssA, ssB); + + Secret bEncryptK; + Secret bMacK; + Secret bEgressMac; + Secret bIngressMac; + { + bytes keyMaterialBytes(512); + bytesConstRef keyMaterial(&keyMaterialBytes); + + h256 ess; + // todo: ecdh-agree should be able to output bytes + s_secp256k1.agree(eB.seckey(), eA.pubkey(), ess); + ess.ref().copyTo(keyMaterial.cropped(0, h256::size)); + ssB.ref().copyTo(keyMaterial.cropped(h256::size, h256::size)); +// auto token = sha3(ssA); + bEncryptK = sha3(keyMaterial); + bEncryptK.ref().copyTo(keyMaterial.cropped(h256::size, h256::size)); + bMacK = sha3(keyMaterial); + + // todo: replace nonceB with decrypted nonceB + keyMaterialBytes.resize(h256::size + ackcipher.size()); + keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); + (bMacK ^ nonceA).ref().copyTo(keyMaterial); + bytesConstRef(&ackcipher).copyTo(keyMaterial.cropped(h256::size, ackcipher.size())); + bEgressMac = sha3(keyMaterial); + + keyMaterialBytes.resize(h256::size + authcipher.size()); + keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); + (bMacK ^ nonceB).ref().copyTo(keyMaterial); + bytesConstRef(&authcipher).copyTo(keyMaterial.cropped(h256::size, authcipher.size())); + bIngressMac = sha3(keyMaterial); + } + BOOST_REQUIRE_EQUAL(aEncryptK, bEncryptK); + BOOST_REQUIRE_EQUAL(aMacK, bMacK); + BOOST_REQUIRE_EQUAL(aEgressMac, bIngressMac); + BOOST_REQUIRE_EQUAL(bEgressMac, aIngressMac); } BOOST_AUTO_TEST_CASE(ecdhe_aes128_ctr_sha3mac) From 068dea54230ee1d7a1d8c15d041b4a02ca43aa97 Mon Sep 17 00:00:00 2001 From: subtly Date: Fri, 13 Feb 2015 22:11:23 -0500 Subject: [PATCH 005/100] finished test of new-node handshake --- test/crypto.cpp | 66 ++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 54 insertions(+), 12 deletions(-) diff --git a/test/crypto.cpp b/test/crypto.cpp index 646202168..d572dc697 100644 --- a/test/crypto.cpp +++ b/test/crypto.cpp @@ -352,14 +352,16 @@ BOOST_AUTO_TEST_CASE(handshakeNew) } bytes authcipher; encrypt(nodeB.pub(), &auth, authcipher); + BOOST_REQUIRE(authcipher.size()); + // Receipient is Bob (nodeB) ECDHE eB; bytes nBbytes(fromHex("0xBBBB")); h256 nonceB(sha3(nAbytes)); - bytes ack(h256::size * 2 + 1); + bytes ack(Public::size + h256::size + 1); { - bytesConstRef epubk(&auth[0], Secret::size); - bytesConstRef nonce(&auth[Secret::size], h256::size); + bytesConstRef epubk(&ack[0], Public::size); + bytesConstRef nonce(&ack[Public::size], h256::size); eB.pubkey().ref().copyTo(epubk); nonceB.ref().copyTo(nonce); @@ -367,18 +369,29 @@ BOOST_AUTO_TEST_CASE(handshakeNew) } bytes ackcipher; encrypt(nodeA.pub(), &ack, ackcipher); + BOOST_REQUIRE(ackcipher.size()); BOOST_REQUIRE(eA.pubkey()); BOOST_REQUIRE(eB.pubkey()); BOOST_REQUIRE_NE(eA.seckey(), eB.seckey()); - /// Alice + /// Alice (after receiving ack) Secret aEncryptK; Secret aMacK; Secret aEgressMac; Secret aIngressMac; { - // TODO: decrypt ackcipher and check decrypted material + bytes ackdecrypted; + decrypt(nodeA.sec(), &ackcipher, ackdecrypted); + BOOST_REQUIRE(ackdecrypted.size()); + bytesConstRef ackRef(&ackdecrypted); + Public eBAck; + h256 nonceBAck; + ackRef.cropped(0, Public::size).copyTo(bytesConstRef(eBAck.data(), Public::size)); + ackRef.cropped(Public::size, h256::size).copyTo(nonceBAck.ref()); + BOOST_REQUIRE_EQUAL(eBAck, eB.pubkey()); + BOOST_REQUIRE_EQUAL(nonceBAck, nonceB); + // TODO: export ess and require equal to b bytes keyMaterialBytes(512); @@ -386,18 +399,17 @@ BOOST_AUTO_TEST_CASE(handshakeNew) h256 ess; // todo: ecdh-agree should be able to output bytes - s_secp256k1.agree(eA.seckey(), eB.pubkey(), ess); + s_secp256k1.agree(eA.seckey(), eBAck, ess); ess.ref().copyTo(keyMaterial.cropped(0, h256::size)); ssA.ref().copyTo(keyMaterial.cropped(h256::size, h256::size)); // auto token = sha3(ssA); aEncryptK = sha3(keyMaterial); aEncryptK.ref().copyTo(keyMaterial.cropped(h256::size, h256::size)); aMacK = sha3(keyMaterial); - - // todo: replace nonceB with decrypted nonceB + keyMaterialBytes.resize(h256::size + authcipher.size()); keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); - (aMacK ^ nonceB).ref().copyTo(keyMaterial); + (aMacK ^ nonceBAck).ref().copyTo(keyMaterial); bytesConstRef(&authcipher).copyTo(keyMaterial.cropped(h256::size, authcipher.size())); aEgressMac = sha3(keyMaterial); @@ -409,7 +421,7 @@ BOOST_AUTO_TEST_CASE(handshakeNew) } - /// Bob + /// Bob (after sending ack) Secret ssB; s_secp256k1.agree(nodeB.sec(), nodeA.pub(), ssB); BOOST_REQUIRE_EQUAL(ssA, ssB); @@ -419,12 +431,42 @@ BOOST_AUTO_TEST_CASE(handshakeNew) Secret bEgressMac; Secret bIngressMac; { + bytes authdecrypted; + decrypt(nodeB.sec(), &authcipher, authdecrypted); + BOOST_REQUIRE(authdecrypted.size()); + bytesConstRef ackRef(&authdecrypted); + Signature sigAuth; + h256 heA; + Public eAAuth; + Public nodeAAuth; + h256 nonceAAuth; + bytesConstRef sig(&authdecrypted[0], Signature::size); + bytesConstRef hepubk(&authdecrypted[Signature::size], h256::size); + bytesConstRef pubk(&authdecrypted[Signature::size + h256::size], Public::size); + bytesConstRef nonce(&authdecrypted[Signature::size + h256::size + Public::size], h256::size); + + nonce.copyTo(nonceAAuth.ref()); + pubk.copyTo(nodeAAuth.ref()); + BOOST_REQUIRE(nonceAAuth); + BOOST_REQUIRE_EQUAL(nonceA, nonceAAuth); + BOOST_REQUIRE(nodeAAuth); + BOOST_REQUIRE_EQUAL(nodeA.pub(), nodeAAuth); // bad test, bad!!! + hepubk.copyTo(heA.ref()); + sig.copyTo(sigAuth.ref()); + + Secret ss; + s_secp256k1.agree(nodeB.sec(), nodeAAuth, ss); + eAAuth = recover(sigAuth, ss ^ nonceAAuth); + // todo: test when this fails; means remote is bad or packet bits were flipped + BOOST_REQUIRE_EQUAL(heA, sha3(eAAuth)); + BOOST_REQUIRE_EQUAL(eAAuth, eA.pubkey()); + bytes keyMaterialBytes(512); bytesConstRef keyMaterial(&keyMaterialBytes); h256 ess; // todo: ecdh-agree should be able to output bytes - s_secp256k1.agree(eB.seckey(), eA.pubkey(), ess); + s_secp256k1.agree(eB.seckey(), eAAuth, ess); ess.ref().copyTo(keyMaterial.cropped(0, h256::size)); ssB.ref().copyTo(keyMaterial.cropped(h256::size, h256::size)); // auto token = sha3(ssA); @@ -435,7 +477,7 @@ BOOST_AUTO_TEST_CASE(handshakeNew) // todo: replace nonceB with decrypted nonceB keyMaterialBytes.resize(h256::size + ackcipher.size()); keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); - (bMacK ^ nonceA).ref().copyTo(keyMaterial); + (bMacK ^ nonceAAuth).ref().copyTo(keyMaterial); bytesConstRef(&ackcipher).copyTo(keyMaterial.cropped(h256::size, ackcipher.size())); bEgressMac = sha3(keyMaterial); From 261c04314f6e4d35024d55a067a8976ad8c91855 Mon Sep 17 00:00:00 2001 From: subtly Date: Mon, 16 Feb 2015 03:24:52 -0500 Subject: [PATCH 006/100] fix snapshot when nodetable #1055 --- libp2p/Host.cpp | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index f117df37a..f4882b47d 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -665,17 +665,20 @@ bytes Host::saveNetwork() const } } - auto state = m_nodeTable->snapshot(); - state.sort(); - for (auto const& s: state) + if (!!m_nodeTable) { - network.appendList(3); - if (s.endpoint.tcp.address().is_v4()) - network << s.endpoint.tcp.address().to_v4().to_bytes(); - else - network << s.endpoint.tcp.address().to_v6().to_bytes(); - network << s.endpoint.tcp.port() << s.id; - count++; + auto state = m_nodeTable->snapshot(); + state.sort(); + for (auto const& s: state) + { + network.appendList(3); + if (s.endpoint.tcp.address().is_v4()) + network << s.endpoint.tcp.address().to_v4().to_bytes(); + else + network << s.endpoint.tcp.address().to_v6().to_bytes(); + network << s.endpoint.tcp.port() << s.id; + count++; + } } RLPStream ret(3); From 4814303c0965173ed720d70792712c7065ab3571 Mon Sep 17 00:00:00 2001 From: subtly Date: Tue, 17 Feb 2015 07:16:56 -0500 Subject: [PATCH 007/100] initial handshake on the wire --- libdevcrypto/Common.cpp | 62 ++++++++++ libdevcrypto/Common.h | 4 + libdevcrypto/CryptoPP.cpp | 2 +- libdevcrypto/ECDHE.cpp | 5 + libdevcrypto/ECDHE.h | 9 +- libp2p/Host.cpp | 239 +++++++++++++++++++++++++++++++++----- libp2p/Host.h | 47 +++++++- test/crypto.cpp | 66 +++++++++-- 8 files changed, 392 insertions(+), 42 deletions(-) diff --git a/libdevcrypto/Common.cpp b/libdevcrypto/Common.cpp index 0a94662c8..44b7d9ad8 100644 --- a/libdevcrypto/Common.cpp +++ b/libdevcrypto/Common.cpp @@ -92,6 +92,68 @@ bool dev::decryptSym(Secret const& _k, bytesConstRef _cipher, bytes& o_plain) return decrypt(_k, _cipher, o_plain); } +h256 dev::encryptSymNoAuth(Secret const& _k, bytesConstRef _plain, bytes& o_cipher) +{ + auto iv = Nonce::get(); + return encryptSymNoAuth(_k, _plain, o_cipher, iv); +} + +h256 dev::encryptSymNoAuth(Secret const& _k, bytesConstRef _plain, bytes& o_cipher, h256 const& _iv) +{ + const int c_aesBlockLen = 16; + o_cipher.resize(_plain.size() + (c_aesBlockLen - (_plain.size() % c_aesBlockLen)) % c_aesBlockLen); + + bytes underflowBytes(0); + auto size = _plain.size() - (_plain.size() % c_aesBlockLen); + if (o_cipher.size() > _plain.size()) + { + underflowBytes.resize(c_aesBlockLen); + _plain.cropped(size, _plain.size() - size).copyTo(&underflowBytes); + } + + const int c_aesKeyLen = 32; + SecByteBlock key(_k.data(), c_aesKeyLen); + try + { + CTR_Mode::Encryption e; + e.SetKeyWithIV(key, key.size(), _iv.data()); + if (size) + e.ProcessData(o_cipher.data(), _plain.data(), _plain.size()); + if (underflowBytes.size()) + e.ProcessData(o_cipher.data(), underflowBytes.data(), underflowBytes.size()); + return _iv; + } + catch(CryptoPP::Exception& e) + { + cerr << e.what() << endl; + o_cipher.resize(0); + return h256(); + } +} + +bool dev::decryptSymNoAuth(Secret const& _k, h256 const& _iv, bytesConstRef _cipher, bytes& o_plaintext) +{ + const int c_aesKeyLen = 32; + const int c_aesBlockLen = 16; + asserts(_cipher.size() % c_aesBlockLen == 0); + o_plaintext.resize(_cipher.size()); + SecByteBlock key(_k.data(), c_aesKeyLen); + + try + { + CTR_Mode::Decryption d; + d.SetKeyWithIV(key, key.size(), _iv.data()); + d.ProcessData(o_plaintext.data(), _cipher.data(), _cipher.size()); + return true; + } + catch(CryptoPP::Exception& e) + { + cerr << e.what() << endl; + o_plaintext.resize(0); + return false; + } +} + Public dev::recover(Signature const& _sig, h256 const& _message) { return s_secp256k1.recover(_sig, _message.ref()); diff --git a/libdevcrypto/Common.h b/libdevcrypto/Common.h index e91df2526..d46161ab8 100644 --- a/libdevcrypto/Common.h +++ b/libdevcrypto/Common.h @@ -93,6 +93,10 @@ void encryptSym(Secret const& _k, bytesConstRef _plain, bytes& o_cipher); /// Symmetric decryption. bool decryptSym(Secret const& _k, bytesConstRef _cipher, bytes& o_plaintext); +h256 encryptSymNoAuth(Secret const& _k, bytesConstRef _plain, bytes& o_cipher); +h256 encryptSymNoAuth(Secret const& _k, bytesConstRef _plain, bytes& o_cipher, h256 const& _iv); +bool decryptSymNoAuth(Secret const& _k, h256 const& _iv, bytesConstRef _cipher, bytes& o_plaintext); + /// Recovers Public key from signed message hash. Public recover(Signature const& _sig, h256 const& _hash); diff --git a/libdevcrypto/CryptoPP.cpp b/libdevcrypto/CryptoPP.cpp index 2d0170fb9..385fec981 100644 --- a/libdevcrypto/CryptoPP.cpp +++ b/libdevcrypto/CryptoPP.cpp @@ -201,7 +201,7 @@ void Secp256k1::agree(Secret const& _s, Public const& _r, h256& o_s) { (void)o_s; (void)_s; - ECDH::Domain d(m_oid); + ECDH::Domain d(ASN1::secp256k1()); assert(d.AgreedValueLength() == sizeof(o_s)); byte remote[65] = {0x04}; memcpy(&remote[1], _r.data(), 64); diff --git a/libdevcrypto/ECDHE.cpp b/libdevcrypto/ECDHE.cpp index deae3bc6d..8f667f0f9 100644 --- a/libdevcrypto/ECDHE.cpp +++ b/libdevcrypto/ECDHE.cpp @@ -29,6 +29,11 @@ using namespace dev::crypto; static Secp256k1 s_secp256k1; +void dev::crypto::ecdh::agree(Secret const& _s, Public const& _r, h256& o_s) +{ + s_secp256k1.agree(_s, _r, o_s); +} + void ECDHE::agree(Public const& _remote, Secret& o_sharedSecret) { if (m_remoteEphemeral) diff --git a/libdevcrypto/ECDHE.h b/libdevcrypto/ECDHE.h index 2a09e8e78..6d01b0329 100644 --- a/libdevcrypto/ECDHE.h +++ b/libdevcrypto/ECDHE.h @@ -48,6 +48,11 @@ private: std::map m_sessions; Secret m_secret; }; + +namespace ecdh +{ +void agree(Secret const& _s, Public const& _r, h256& o_s); +} /** * @brief Derive DH shared secret from EC keypairs. @@ -82,10 +87,10 @@ class ECDHEKeyExchange: private ECDHE { public: /// Exchange with unknown remote (pass public key for ingress exchange) - ECDHEKeyExchange(Alias& _k): m_alias(_k) {}; + ECDHEKeyExchange(Alias& _k): m_alias(_k) {} /// Exchange with known remote - ECDHEKeyExchange(Alias& _k, AliasSession _known): m_alias(_k), m_known(_known) {}; + ECDHEKeyExchange(Alias& _k, AliasSession _known): m_alias(_k), m_known(_known) {} /// Provide public key for dh agreement to generate shared secret. void agree(Public const& _remoteEphemeral); diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index f4882b47d..c91cedaf3 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -337,7 +337,7 @@ void Host::runAcceptor() { // doHandshake takes ownersihp of *s via std::move // incoming connection; we don't yet know nodeid - doHandshake(s, NodeId()); + doHandshake(new Handshake(s)); success = true; } catch (Exception const& _e) @@ -351,15 +351,18 @@ void Host::runAcceptor() } // asio doesn't close socket on error - if (!success && s->is_open()) + if (!success) { - boost::system::error_code ec; - s->shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); - s->close(); + if (s->is_open()) + { + boost::system::error_code ec; + s->shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); + s->close(); + } + delete s; } m_accepting = false; - delete s; if (ec.value() < 1) runAcceptor(); @@ -367,22 +370,209 @@ void Host::runAcceptor() } } -void Host::doHandshake(bi::tcp::socket* _socket, NodeId _nodeId) +void Host::doHandshake(Handshake* _h, boost::system::error_code _ech) { - try { - clog(NetConnect) << "Accepting connection for " << _socket->remote_endpoint(); - } catch (...){} - - shared_ptr p; - if (_nodeId) - p = m_peers[_nodeId]; + if (_ech) + { + boost::system::error_code ec; + _h->socket->shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); + if (_h->socket->is_open()) + _h->socket->close(); + delete _h; + return; + } - if (!p) - p.reset(new Peer()); - p->endpoint.tcp.address(_socket->remote_endpoint().address()); - - auto ps = std::make_shared(this, std::move(*_socket), p); - ps->start(); + if (!_h->started()) + { + clog(NetConnect) << "Authenticating connection for " << _h->socket->remote_endpoint(); + + if (_h->originated) + { + clog(NetConnect) << "devp2p.connect.egress sending auth"; + // egress: tx auth + asserts(_h->remote); + _h->auth.resize(Signature::size + h256::size + Public::size + h256::size + 1); + bytesConstRef sig(&_h->auth[0], Signature::size); + bytesConstRef hepubk(&_h->auth[Signature::size], h256::size); + bytesConstRef pubk(&_h->auth[Signature::size + h256::size], Public::size); + bytesConstRef nonce(&_h->auth[Signature::size + h256::size + Public::size], h256::size); + + // E(remote-pubk, S(ecdhe-random, ecdh-shared-secret^nonce) || H(ecdhe-random-pubk) || pubk || nonce || 0x0) + crypto::ecdh::agree(m_alias.sec(), _h->remote, _h->ss); + sign(_h->ecdhe.seckey(), _h->ss ^ _h->nonce).ref().copyTo(sig); + sha3(_h->ecdhe.pubkey().ref(), hepubk); + m_alias.pub().ref().copyTo(pubk); + _h->nonce.ref().copyTo(nonce); + _h->auth[_h->auth.size() - 1] = 0x0; + encrypt(_h->remote, &_h->auth, _h->authCipher); + ba::async_write(*_h->socket, ba::buffer(_h->authCipher), [=](boost::system::error_code ec, std::size_t) + { + doHandshake(_h, ec); + }); + } + else + { + clog(NetConnect) << "devp2p.connect.ingress recving auth"; + // ingress: rx auth + _h->authCipher.resize(279); + ba::async_read(*_h->socket, ba::buffer(_h->authCipher, 279), [=](boost::system::error_code ec, std::size_t) + { + if (ec) + doHandshake(_h, ec); + else + { + decrypt(m_alias.sec(), bytesConstRef(&_h->authCipher), _h->auth); + bytesConstRef sig(&_h->auth[0], Signature::size); + bytesConstRef hepubk(&_h->auth[Signature::size], h256::size); + bytesConstRef pubk(&_h->auth[Signature::size + h256::size], Public::size); + bytesConstRef nonce(&_h->auth[Signature::size + h256::size + Public::size], h256::size); + pubk.copyTo(_h->remote.ref()); + nonce.copyTo(_h->remoteNonce.ref()); + + crypto::ecdh::agree(m_alias.sec(), _h->remote, _h->ss); + _h->remoteEphemeral = recover(*(Signature*)sig.data(), _h->ss ^ _h->remoteNonce); + assert(sha3(_h->remoteEphemeral) == *(h256*)hepubk.data()); + doHandshake(_h); + } + }); + } + } + else if (_h->started() && !_h->acked()) + if (_h->originated) + { + clog(NetConnect) << "devp2p.connect.egress recving ack"; + // egress: rx ack + _h->ackCipher.resize(182); + ba::async_read(*_h->socket, ba::buffer(_h->ackCipher, 182), [=](boost::system::error_code ec, std::size_t) + { + if (ec) + doHandshake(_h, ec); + else + { + decrypt(m_alias.sec(), bytesConstRef(&_h->ackCipher), _h->ack); + bytesConstRef(&_h->ack).cropped(0, Public::size).copyTo(_h->remoteEphemeral.ref()); + bytesConstRef(&_h->ack).cropped(Public::size, h256::size).copyTo(_h->remoteNonce.ref()); + doHandshake(_h); + } + }); + } + else + { + clog(NetConnect) << "devp2p.connect.ingress sending ack"; + // ingress: tx ack + _h->ack.resize(Public::size + h256::size + 1); + bytesConstRef epubk(&_h->ack[0], Public::size); + bytesConstRef nonce(&_h->ack[Public::size], h256::size); + _h->ecdhe.pubkey().ref().copyTo(epubk); + _h->nonce.ref().copyTo(nonce); + _h->ack[_h->ack.size() - 1] = 0x0; + encrypt(_h->remote, &_h->ack, _h->ackCipher); + ba::async_write(*_h->socket, ba::buffer(_h->ackCipher), [=](boost::system::error_code ec, std::size_t) + { + doHandshake(_h, ec); + }); + } + else if (_h->started() && _h->acked()) + { + if (_h->originated) + clog(NetConnect) << "devp2p.connect.egress sending magic sequence"; + else + clog(NetConnect) << "devp2p.connect.ingress sending magic sequence"; + PeerSecrets* k = new PeerSecrets; + bytes keyMaterialBytes(512); + bytesConstRef keyMaterial(&keyMaterialBytes); + + _h->ecdhe.agree(_h->remoteEphemeral, _h->ess); + _h->ess.ref().copyTo(keyMaterial.cropped(0, h256::size)); + _h->ss.ref().copyTo(keyMaterial.cropped(h256::size, h256::size)); + // auto token = sha3(ssA); + k->encryptK = sha3(keyMaterial); + k->encryptK.ref().copyTo(keyMaterial.cropped(h256::size, h256::size)); + k->macK = sha3(keyMaterial); + + // Initiator egress-mac: sha3(mac-secret^recipient-nonce || auth-sent-init) + // ingress-mac: sha3(mac-secret^initiator-nonce || auth-recvd-ack) + // Recipient egress-mac: sha3(mac-secret^initiator-nonce || auth-sent-ack) + // ingress-mac: sha3(mac-secret^recipient-nonce || auth-recvd-init) + + bytes const& egressCipher = _h->originated ? _h->authCipher : _h->ackCipher; + keyMaterialBytes.resize(h256::size + egressCipher.size()); + keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); + (k->macK ^ _h->remoteNonce).ref().copyTo(keyMaterial); + bytesConstRef(&egressCipher).copyTo(keyMaterial.cropped(h256::size, egressCipher.size())); + k->egressMac = sha3(keyMaterial); + + bytes const& ingressCipher = _h->originated ? _h->ackCipher : _h->authCipher; + keyMaterialBytes.resize(h256::size + ingressCipher.size()); + keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); + (k->macK ^ _h->nonce).ref().copyTo(keyMaterial); + bytesConstRef(&ingressCipher).copyTo(keyMaterial.cropped(h256::size, ingressCipher.size())); + k->ingressMac = sha3(keyMaterial); + + // TESTING: send encrypt magic sequence + bytes magic {0x22,0x40,0x08,0x91}; + encryptSymNoAuth(k->encryptK, &magic, k->magicCipherAndMac, h256()); + k->magicCipherAndMac.resize(k->magicCipherAndMac.size() + 32); + sha3mac(k->egressMac.ref(), &magic, k->egressMac.ref()); + k->egressMac.ref().copyTo(bytesConstRef(&k->magicCipherAndMac).cropped(k->magicCipherAndMac.size() - 32, 32)); + + clog(NetConnect) << "devp2p.connect.egress txrx magic sequence"; + + ba::async_write(*_h->socket, ba::buffer(k->magicCipherAndMac), [this, k, _h, magic](boost::system::error_code ec, std::size_t) + { + if (ec) + { + delete k; + doHandshake(_h, ec); + return; + } + + ba::async_read(*_h->socket, ba::buffer(k->recvdMagicCipherAndMac, k->magicCipherAndMac.size()), [this, k, _h, magic](boost::system::error_code ec, std::size_t) + { + if (_h->originated) + clog(NetNote) << "devp2p.connect.egress recving magic sequence"; + else + clog(NetNote) << "devp2p.connect.ingress recving magic sequence"; + + if (ec) + { + delete k; + doHandshake(_h, ec); + return; + } + + bytes decryptedMagic; + decryptSymNoAuth(k->encryptK, h256(), &k->recvdMagicCipherAndMac, decryptedMagic); + clog(NetNote) << "devp2p.connect received magic sequence"; + + shared_ptr p; + p = m_peers[_h->remote]; + + if (!p) + { + p.reset(new Peer()); + p->id = _h->remote; + } + p->endpoint.tcp.address(_h->socket->remote_endpoint().address()); + p->m_lastDisconnect = NoDisconnect; + p->m_lastConnected = std::chrono::system_clock::now(); + p->m_failedAttempts = 0; + + auto ps = std::make_shared(this, move(*_h->socket), p); + ps->start(); + + delete k; + }); + }); + } + else + { + clog(NetConnect) << "Disconnecting " << _h->socket->remote_endpoint() << " (Authentication Failed)"; + boost::system::error_code ec; + _h->socket->shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); + _h->socket->close(); + delete _h; + } } string Host::pocHost() @@ -467,18 +657,13 @@ void Host::connect(std::shared_ptr const& _p) clog(NetConnect) << "Connection refused to node" << _p->id.abridged() << "@" << _p->peerEndpoint() << "(" << ec.message() << ")"; _p->m_lastDisconnect = TCPError; _p->m_lastAttempted = std::chrono::system_clock::now(); + delete s; } else { clog(NetConnect) << "Connected to" << _p->id.abridged() << "@" << _p->peerEndpoint(); - _p->m_lastDisconnect = NoDisconnect; - _p->m_lastConnected = std::chrono::system_clock::now(); - _p->m_failedAttempts = 0; - auto ps = make_shared(this, std::move(*s), _p); - ps->start(); - + doHandshake(new Handshake(s, _p->id)); } - delete s; Guard l(x_pendingNodeConns); m_pendingPeerConns.erase(nptr); }); diff --git a/libp2p/Host.h b/libp2p/Host.h index baf8f0585..3ca414c88 100644 --- a/libp2p/Host.h +++ b/libp2p/Host.h @@ -34,6 +34,7 @@ #include #include #include +#include #include "NodeTable.h" #include "HostCapability.h" #include "Network.h" @@ -157,6 +158,48 @@ protected: void restoreNetwork(bytesConstRef _b); private: + struct Handshake + { + /// Handshake for ingress connection. Takes ownership of socket. + Handshake(bi::tcp::socket* _socket): socket(std::move(_socket)), originated(false) { crypto::Nonce::get().ref().copyTo(nonce.ref()); } + + /// Handshake for egress connection to _remote. Takes ownership of socket. + Handshake(bi::tcp::socket* _socket, NodeId _remote): socket(std::move(_socket)), originated(true), remote(_remote) { crypto::Nonce::get().ref().copyTo(nonce.ref()); } + + ~Handshake() { delete socket; } + + bool started() { return auth.size() > 0; } + bool acked() { return ack.size() > 0; } + + /// If originated this is accepting (ingress) node id, otherwise it is originating (egress) node. + NodeId remote; + bi::tcp::socket *socket; + bool originated = false; + + bytes auth; + bytes authCipher; + bytes ack; + bytes ackCipher; + Secret ss; + Secret ess; + + crypto::ECDHE ecdhe; + h256 nonce; + + Public remoteEphemeral; + h256 remoteNonce; + }; + struct PeerSecrets + { + Secret encryptK; + Secret macK; + h256 egressMac; + h256 ingressMac; + + bytes magicCipherAndMac; + bytes recvdMagicCipherAndMac; + }; + /// Populate m_peerAddresses with available public addresses. void determinePublic(std::string const& _publicAddress, bool _upnp); @@ -171,8 +214,8 @@ private: /// Called only from startedWorking(). void runAcceptor(); - /// Handler for verifying handshake siganture before creating session. _nodeId is passed for outbound connections. If successful, socket is moved to Session via std::move. - void doHandshake(bi::tcp::socket* _socket, NodeId _nodeId = NodeId()); + /// Attempt to authenticate peer and establish a new session. + void doHandshake(Handshake* _h, boost::system::error_code _ec = boost::system::error_code()); void seal(bytes& _b); diff --git a/test/crypto.cpp b/test/crypto.cpp index d572dc697..5dec3dd6d 100644 --- a/test/crypto.cpp +++ b/test/crypto.cpp @@ -321,13 +321,16 @@ BOOST_AUTO_TEST_CASE(handshakeNew) // authInitiator -> E(remote-pubk, S(ecdhe-random, ecdh-shared-secret^nonce) || H(ecdhe-random-pubk) || pubk || nonce || 0x0) // authRecipient -> E(remote-pubk, ecdhe-random-pubk || nonce || 0x0) - Secret nodeAsecret(sha3("privacy")); + h256 base(sha3("privacy")); + sha3(base.ref(), base.ref()); + Secret nodeAsecret(base); KeyPair nodeA(nodeAsecret); BOOST_REQUIRE(nodeA.pub()); - Secret nodeBsecret(sha3("privacy++")); + sha3(base.ref(), base.ref()); + Secret nodeBsecret(base); KeyPair nodeB(nodeBsecret); - BOOST_REQUIRE(nodeA.pub()); + BOOST_REQUIRE(nodeB.pub()); BOOST_REQUIRE_NE(nodeA.sec(), nodeB.sec()); @@ -343,7 +346,7 @@ BOOST_AUTO_TEST_CASE(handshakeNew) bytesConstRef pubk(&auth[Signature::size + h256::size], Public::size); bytesConstRef nonce(&auth[Signature::size + h256::size + Public::size], h256::size); - s_secp256k1.agree(nodeA.sec(), nodeB.pub(), ssA); + crypto::ecdh::agree(nodeA.sec(), nodeB.pub(), ssA); sign(eA.seckey(), ssA ^ nonceA).ref().copyTo(sig); sha3(eA.pubkey().ref(), hepubk); nodeA.pub().ref().copyTo(pubk); @@ -352,7 +355,7 @@ BOOST_AUTO_TEST_CASE(handshakeNew) } bytes authcipher; encrypt(nodeB.pub(), &auth, authcipher); - BOOST_REQUIRE(authcipher.size()); + BOOST_REQUIRE_EQUAL(authcipher.size(), 279); // Receipient is Bob (nodeB) ECDHE eB; @@ -360,6 +363,14 @@ BOOST_AUTO_TEST_CASE(handshakeNew) h256 nonceB(sha3(nAbytes)); bytes ack(Public::size + h256::size + 1); { + // todo: replace nodeA.pub() in encrypt() + // decrypt public key from auth + bytes authdecrypted; + decrypt(nodeB.sec(), &authcipher, authdecrypted); + Public node; + bytesConstRef pubk(&authdecrypted[Signature::size + h256::size], Public::size); + pubk.copyTo(node.ref()); + bytesConstRef epubk(&ack[0], Public::size); bytesConstRef nonce(&ack[Public::size], h256::size); @@ -369,7 +380,7 @@ BOOST_AUTO_TEST_CASE(handshakeNew) } bytes ackcipher; encrypt(nodeA.pub(), &ack, ackcipher); - BOOST_REQUIRE(ackcipher.size()); + BOOST_REQUIRE_EQUAL(ackcipher.size(), 182); BOOST_REQUIRE(eA.pubkey()); BOOST_REQUIRE(eB.pubkey()); @@ -399,7 +410,7 @@ BOOST_AUTO_TEST_CASE(handshakeNew) h256 ess; // todo: ecdh-agree should be able to output bytes - s_secp256k1.agree(eA.seckey(), eBAck, ess); + eA.agree(eBAck, ess); ess.ref().copyTo(keyMaterial.cropped(0, h256::size)); ssA.ref().copyTo(keyMaterial.cropped(h256::size, h256::size)); // auto token = sha3(ssA); @@ -423,7 +434,7 @@ BOOST_AUTO_TEST_CASE(handshakeNew) /// Bob (after sending ack) Secret ssB; - s_secp256k1.agree(nodeB.sec(), nodeA.pub(), ssB); + crypto::ecdh::agree(nodeB.sec(), nodeA.pub(), ssB); BOOST_REQUIRE_EQUAL(ssA, ssB); Secret bEncryptK; @@ -466,7 +477,8 @@ BOOST_AUTO_TEST_CASE(handshakeNew) h256 ess; // todo: ecdh-agree should be able to output bytes - s_secp256k1.agree(eB.seckey(), eAAuth, ess); + eB.agree(eAAuth, ess); +// s_secp256k1.agree(eB.seckey(), eAAuth, ess); ess.ref().copyTo(keyMaterial.cropped(0, h256::size)); ssB.ref().copyTo(keyMaterial.cropped(h256::size, h256::size)); // auto token = sha3(ssA); @@ -492,6 +504,9 @@ BOOST_AUTO_TEST_CASE(handshakeNew) BOOST_REQUIRE_EQUAL(aMacK, bMacK); BOOST_REQUIRE_EQUAL(aEgressMac, bIngressMac); BOOST_REQUIRE_EQUAL(bEgressMac, aIngressMac); + + + } BOOST_AUTO_TEST_CASE(ecdhe_aes128_ctr_sha3mac) @@ -504,6 +519,37 @@ BOOST_AUTO_TEST_CASE(ecdhe_aes128_ctr_sha3mac) } +BOOST_AUTO_TEST_CASE(crypto_rlpxwire) +{ + Secret encryptK(sha3("...")); + h256 egressMac(sha3("+++")); + // TESTING: send encrypt magic sequence + bytes magic {0x22,0x40,0x08,0x91}; + bytes magicCipherAndMac; + encryptSymNoAuth(encryptK, &magic, magicCipherAndMac, h256()); + magicCipherAndMac.resize(magicCipherAndMac.size() + 32); + sha3mac(egressMac.ref(), &magic, egressMac.ref()); + egressMac.ref().copyTo(bytesConstRef(&magicCipherAndMac).cropped(magicCipherAndMac.size() - 32, 32)); + + bytes plaintext; + bytesConstRef cipher(&magicCipherAndMac[0], magicCipherAndMac.size() - 32); + decryptSymNoAuth(encryptK, h256(), cipher, plaintext); +} + +BOOST_AUTO_TEST_CASE(crypto_aes128_ctr) +{ + Secret k(sha3("0xAAAA")); + string m = "AAAAAAAAAAAAAAAA"; + bytesConstRef msg((byte*)m.data(), m.size()); + + bytes ciphertext; + auto iv = encryptSymNoAuth(k, msg, ciphertext); + + bytes plaintext; + decryptSymNoAuth(k, iv, &ciphertext, plaintext); + BOOST_REQUIRE_EQUAL(asString(plaintext), m); +} + BOOST_AUTO_TEST_CASE(cryptopp_aes128_ctr) { const int aesKeyLen = 16; @@ -535,7 +581,7 @@ BOOST_AUTO_TEST_CASE(cryptopp_aes128_ctr) // 68 % 255 should be difference of counter e.ProcessData(out, in, text.size()); - ctr = h128(u128(ctr) + text.size() % 16); + ctr = h128(u128(ctr) + text.size() / 16); BOOST_REQUIRE(text != original); cipherCopy = text; From ec4ba8e53231f57792428049114d2a7c7cf17eb5 Mon Sep 17 00:00:00 2001 From: subtly Date: Tue, 17 Feb 2015 07:40:59 -0500 Subject: [PATCH 008/100] fix magic. disable ping for now, as it can trigger prior to hello packet. --- libp2p/Host.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index c91cedaf3..de3a32dd7 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -517,6 +517,7 @@ void Host::doHandshake(Handshake* _h, boost::system::error_code _ech) k->egressMac.ref().copyTo(bytesConstRef(&k->magicCipherAndMac).cropped(k->magicCipherAndMac.size() - 32, 32)); clog(NetConnect) << "devp2p.connect.egress txrx magic sequence"; + k->recvdMagicCipherAndMac.resize(k->magicCipherAndMac.size()); ba::async_write(*_h->socket, ba::buffer(k->magicCipherAndMac), [this, k, _h, magic](boost::system::error_code ec, std::size_t) { @@ -543,8 +544,7 @@ void Host::doHandshake(Handshake* _h, boost::system::error_code _ech) bytes decryptedMagic; decryptSymNoAuth(k->encryptK, h256(), &k->recvdMagicCipherAndMac, decryptedMagic); - clog(NetNote) << "devp2p.connect received magic sequence"; - + shared_ptr p; p = m_peers[_h->remote]; @@ -716,8 +716,8 @@ void Host::run(boost::system::error_code const&) if (auto pp = p.second.lock()) pp->serviceNodesRequest(); - keepAlivePeers(); - disconnectLatePeers(); +// keepAlivePeers(); +// disconnectLatePeers(); auto c = peerCount(); if (m_idealPeerCount && !c) From ecb06feb85d7926d4f2dce518e9f98a2d43a1819 Mon Sep 17 00:00:00 2001 From: subtly Date: Tue, 17 Feb 2015 18:57:25 -0500 Subject: [PATCH 009/100] verification of and placeholder for capabilities handshake --- libp2p/Host.cpp | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index de3a32dd7..9a48817fc 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -542,24 +542,27 @@ void Host::doHandshake(Handshake* _h, boost::system::error_code _ech) return; } + /// capabilities handshake (encrypted magic sequence is placeholder) bytes decryptedMagic; decryptSymNoAuth(k->encryptK, h256(), &k->recvdMagicCipherAndMac, decryptedMagic); - - shared_ptr p; - p = m_peers[_h->remote]; - - if (!p) + if (decryptedMagic[0] == 0x22 && decryptedMagic[1] == 0x40 && decryptedMagic[2] == 0x08 && decryptedMagic[3] == 0x91) { - p.reset(new Peer()); - p->id = _h->remote; + shared_ptr p; + p = m_peers[_h->remote]; + + if (!p) + { + p.reset(new Peer()); + p->id = _h->remote; + } + p->endpoint.tcp.address(_h->socket->remote_endpoint().address()); + p->m_lastDisconnect = NoDisconnect; + p->m_lastConnected = std::chrono::system_clock::now(); + p->m_failedAttempts = 0; + + auto ps = std::make_shared(this, move(*_h->socket), p); + ps->start(); } - p->endpoint.tcp.address(_h->socket->remote_endpoint().address()); - p->m_lastDisconnect = NoDisconnect; - p->m_lastConnected = std::chrono::system_clock::now(); - p->m_failedAttempts = 0; - - auto ps = std::make_shared(this, move(*_h->socket), p); - ps->start(); delete k; }); From f6d5aa75a0221d6396cb685e4aa02308e05694a2 Mon Sep 17 00:00:00 2001 From: subtly Date: Tue, 17 Feb 2015 21:35:10 -0500 Subject: [PATCH 010/100] refactor of handshake. first pass, compiles. --- libp2p/Host.cpp | 375 +++++++++++++++++++++++++++++++++++++----------- libp2p/Host.h | 121 ++++++++++------ libp2p/Peer.h | 3 +- 3 files changed, 371 insertions(+), 128 deletions(-) diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index 9a48817fc..1b6d8d65b 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -337,7 +338,8 @@ void Host::runAcceptor() { // doHandshake takes ownersihp of *s via std::move // incoming connection; we don't yet know nodeid - doHandshake(new Handshake(s)); + auto handshake = make_shared(m_alias, s); + handshake->start(); success = true; } catch (Exception const& _e) @@ -370,89 +372,89 @@ void Host::runAcceptor() } } -void Host::doHandshake(Handshake* _h, boost::system::error_code _ech) -{ +void PeerHandshake::transition(boost::system::error_code _ech) { if (_ech) { boost::system::error_code ec; - _h->socket->shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); - if (_h->socket->is_open()) - _h->socket->close(); - delete _h; + socket->shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); + if (socket->is_open()) + socket->close(); return; } - if (!_h->started()) + auto self(shared_from_this()); + if (isNew()) { - clog(NetConnect) << "Authenticating connection for " << _h->socket->remote_endpoint(); + clog(NetConnect) << "Authenticating connection for " << socket->remote_endpoint(); - if (_h->originated) + if (originated) { clog(NetConnect) << "devp2p.connect.egress sending auth"; // egress: tx auth - asserts(_h->remote); - _h->auth.resize(Signature::size + h256::size + Public::size + h256::size + 1); - bytesConstRef sig(&_h->auth[0], Signature::size); - bytesConstRef hepubk(&_h->auth[Signature::size], h256::size); - bytesConstRef pubk(&_h->auth[Signature::size + h256::size], Public::size); - bytesConstRef nonce(&_h->auth[Signature::size + h256::size + Public::size], h256::size); + asserts(remote); + auth.resize(Signature::size + h256::size + Public::size + h256::size + 1); + bytesConstRef sig(&auth[0], Signature::size); + bytesConstRef hepubk(&auth[Signature::size], h256::size); + bytesConstRef pubk(&auth[Signature::size + h256::size], Public::size); + bytesConstRef nonce(&auth[Signature::size + h256::size + Public::size], h256::size); // E(remote-pubk, S(ecdhe-random, ecdh-shared-secret^nonce) || H(ecdhe-random-pubk) || pubk || nonce || 0x0) - crypto::ecdh::agree(m_alias.sec(), _h->remote, _h->ss); - sign(_h->ecdhe.seckey(), _h->ss ^ _h->nonce).ref().copyTo(sig); - sha3(_h->ecdhe.pubkey().ref(), hepubk); - m_alias.pub().ref().copyTo(pubk); - _h->nonce.ref().copyTo(nonce); - _h->auth[_h->auth.size() - 1] = 0x0; - encrypt(_h->remote, &_h->auth, _h->authCipher); - ba::async_write(*_h->socket, ba::buffer(_h->authCipher), [=](boost::system::error_code ec, std::size_t) + crypto::ecdh::agree(alias.sec(), remote, ss); + sign(ecdhe.seckey(), ss ^ this->nonce).ref().copyTo(sig); + sha3(ecdhe.pubkey().ref(), hepubk); + alias.pub().ref().copyTo(pubk); + this->nonce.ref().copyTo(nonce); + auth[auth.size() - 1] = 0x0; + encrypt(remote, &auth, authCipher); + + ba::async_write(*socket, ba::buffer(authCipher), [this, self](boost::system::error_code ec, std::size_t) { - doHandshake(_h, ec); + transition(ec); }); } else { clog(NetConnect) << "devp2p.connect.ingress recving auth"; // ingress: rx auth - _h->authCipher.resize(279); - ba::async_read(*_h->socket, ba::buffer(_h->authCipher, 279), [=](boost::system::error_code ec, std::size_t) + authCipher.resize(279); + ba::async_read(*socket, ba::buffer(authCipher, 279), [this, self](boost::system::error_code ec, std::size_t) { if (ec) - doHandshake(_h, ec); + transition(ec); else { - decrypt(m_alias.sec(), bytesConstRef(&_h->authCipher), _h->auth); - bytesConstRef sig(&_h->auth[0], Signature::size); - bytesConstRef hepubk(&_h->auth[Signature::size], h256::size); - bytesConstRef pubk(&_h->auth[Signature::size + h256::size], Public::size); - bytesConstRef nonce(&_h->auth[Signature::size + h256::size + Public::size], h256::size); - pubk.copyTo(_h->remote.ref()); - nonce.copyTo(_h->remoteNonce.ref()); + decrypt(alias.sec(), bytesConstRef(&authCipher), auth); + bytesConstRef sig(&auth[0], Signature::size); + bytesConstRef hepubk(&auth[Signature::size], h256::size); + bytesConstRef pubk(&auth[Signature::size + h256::size], Public::size); + bytesConstRef nonce(&auth[Signature::size + h256::size + Public::size], h256::size); + pubk.copyTo(remote.ref()); + nonce.copyTo(remoteNonce.ref()); - crypto::ecdh::agree(m_alias.sec(), _h->remote, _h->ss); - _h->remoteEphemeral = recover(*(Signature*)sig.data(), _h->ss ^ _h->remoteNonce); - assert(sha3(_h->remoteEphemeral) == *(h256*)hepubk.data()); - doHandshake(_h); + crypto::ecdh::agree(alias.sec(), remote, ss); + remoteEphemeral = recover(*(Signature*)sig.data(), ss ^ remoteNonce); + assert(sha3(remoteEphemeral) == *(h256*)hepubk.data()); + transition(); } }); } } - else if (_h->started() && !_h->acked()) - if (_h->originated) + else if (isAcking()) + if (originated) { clog(NetConnect) << "devp2p.connect.egress recving ack"; // egress: rx ack - _h->ackCipher.resize(182); - ba::async_read(*_h->socket, ba::buffer(_h->ackCipher, 182), [=](boost::system::error_code ec, std::size_t) + ackCipher.resize(182); + ba::async_read(*socket, ba::buffer(ackCipher, 182), [this, self](boost::system::error_code ec, std::size_t) { if (ec) - doHandshake(_h, ec); + transition(ec); else { - decrypt(m_alias.sec(), bytesConstRef(&_h->ackCipher), _h->ack); - bytesConstRef(&_h->ack).cropped(0, Public::size).copyTo(_h->remoteEphemeral.ref()); - bytesConstRef(&_h->ack).cropped(Public::size, h256::size).copyTo(_h->remoteNonce.ref()); - doHandshake(_h); + decrypt(alias.sec(), bytesConstRef(&ackCipher), ack); + bytesConstRef(&ack).cropped(0, Public::size).copyTo(remoteEphemeral.ref()); + bytesConstRef(&ack).cropped(Public::size, h256::size).copyTo(remoteNonce.ref()); + transition(); } }); } @@ -460,21 +462,21 @@ void Host::doHandshake(Handshake* _h, boost::system::error_code _ech) { clog(NetConnect) << "devp2p.connect.ingress sending ack"; // ingress: tx ack - _h->ack.resize(Public::size + h256::size + 1); - bytesConstRef epubk(&_h->ack[0], Public::size); - bytesConstRef nonce(&_h->ack[Public::size], h256::size); - _h->ecdhe.pubkey().ref().copyTo(epubk); - _h->nonce.ref().copyTo(nonce); - _h->ack[_h->ack.size() - 1] = 0x0; - encrypt(_h->remote, &_h->ack, _h->ackCipher); - ba::async_write(*_h->socket, ba::buffer(_h->ackCipher), [=](boost::system::error_code ec, std::size_t) + ack.resize(Public::size + h256::size + 1); + bytesConstRef epubk(&ack[0], Public::size); + bytesConstRef nonce(&ack[Public::size], h256::size); + ecdhe.pubkey().ref().copyTo(epubk); + this->nonce.ref().copyTo(nonce); + ack[ack.size() - 1] = 0x0; + encrypt(remote, &ack, ackCipher); + ba::async_write(*socket, ba::buffer(ackCipher), [this, self](boost::system::error_code ec, std::size_t) { - doHandshake(_h, ec); + transition(ec); }); } - else if (_h->started() && _h->acked()) + else if (isAuthenticating()) { - if (_h->originated) + if (originated) clog(NetConnect) << "devp2p.connect.egress sending magic sequence"; else clog(NetConnect) << "devp2p.connect.ingress sending magic sequence"; @@ -482,9 +484,9 @@ void Host::doHandshake(Handshake* _h, boost::system::error_code _ech) bytes keyMaterialBytes(512); bytesConstRef keyMaterial(&keyMaterialBytes); - _h->ecdhe.agree(_h->remoteEphemeral, _h->ess); - _h->ess.ref().copyTo(keyMaterial.cropped(0, h256::size)); - _h->ss.ref().copyTo(keyMaterial.cropped(h256::size, h256::size)); + ecdhe.agree(remoteEphemeral, ess); + ess.ref().copyTo(keyMaterial.cropped(0, h256::size)); + ss.ref().copyTo(keyMaterial.cropped(h256::size, h256::size)); // auto token = sha3(ssA); k->encryptK = sha3(keyMaterial); k->encryptK.ref().copyTo(keyMaterial.cropped(h256::size, h256::size)); @@ -495,17 +497,17 @@ void Host::doHandshake(Handshake* _h, boost::system::error_code _ech) // Recipient egress-mac: sha3(mac-secret^initiator-nonce || auth-sent-ack) // ingress-mac: sha3(mac-secret^recipient-nonce || auth-recvd-init) - bytes const& egressCipher = _h->originated ? _h->authCipher : _h->ackCipher; + bytes const& egressCipher = originated ? authCipher : ackCipher; keyMaterialBytes.resize(h256::size + egressCipher.size()); keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); - (k->macK ^ _h->remoteNonce).ref().copyTo(keyMaterial); + (k->macK ^ remoteNonce).ref().copyTo(keyMaterial); bytesConstRef(&egressCipher).copyTo(keyMaterial.cropped(h256::size, egressCipher.size())); k->egressMac = sha3(keyMaterial); - bytes const& ingressCipher = _h->originated ? _h->ackCipher : _h->authCipher; + bytes const& ingressCipher = originated ? ackCipher : authCipher; keyMaterialBytes.resize(h256::size + ingressCipher.size()); keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); - (k->macK ^ _h->nonce).ref().copyTo(keyMaterial); + (k->macK ^ nonce).ref().copyTo(keyMaterial); bytesConstRef(&ingressCipher).copyTo(keyMaterial.cropped(h256::size, ingressCipher.size())); k->ingressMac = sha3(keyMaterial); @@ -519,18 +521,18 @@ void Host::doHandshake(Handshake* _h, boost::system::error_code _ech) clog(NetConnect) << "devp2p.connect.egress txrx magic sequence"; k->recvdMagicCipherAndMac.resize(k->magicCipherAndMac.size()); - ba::async_write(*_h->socket, ba::buffer(k->magicCipherAndMac), [this, k, _h, magic](boost::system::error_code ec, std::size_t) + ba::async_write(*socket, ba::buffer(k->magicCipherAndMac), [this, self, k, magic](boost::system::error_code ec, std::size_t) { if (ec) { delete k; - doHandshake(_h, ec); + transition(ec); return; } - ba::async_read(*_h->socket, ba::buffer(k->recvdMagicCipherAndMac, k->magicCipherAndMac.size()), [this, k, _h, magic](boost::system::error_code ec, std::size_t) + ba::async_read(*socket, ba::buffer(k->recvdMagicCipherAndMac, k->magicCipherAndMac.size()), [this, self, k, magic](boost::system::error_code ec, std::size_t) { - if (_h->originated) + if (originated) clog(NetNote) << "devp2p.connect.egress recving magic sequence"; else clog(NetNote) << "devp2p.connect.ingress recving magic sequence"; @@ -538,7 +540,7 @@ void Host::doHandshake(Handshake* _h, boost::system::error_code _ech) if (ec) { delete k; - doHandshake(_h, ec); + transition(ec); return; } @@ -548,36 +550,246 @@ void Host::doHandshake(Handshake* _h, boost::system::error_code _ech) if (decryptedMagic[0] == 0x22 && decryptedMagic[1] == 0x40 && decryptedMagic[2] == 0x08 && decryptedMagic[3] == 0x91) { shared_ptr p; - p = m_peers[_h->remote]; + // todo: need host +// p = m_peers[remote]; if (!p) { p.reset(new Peer()); - p->id = _h->remote; + p->id = remote; } - p->endpoint.tcp.address(_h->socket->remote_endpoint().address()); + p->endpoint.tcp.address(socket->remote_endpoint().address()); p->m_lastDisconnect = NoDisconnect; p->m_lastConnected = std::chrono::system_clock::now(); p->m_failedAttempts = 0; - auto ps = std::make_shared(this, move(*_h->socket), p); - ps->start(); + // todo: need host +// auto ps = std::make_shared(this, move(*socket), p); +// ps->start(); } + // todo: PeerSession needs to take ownership of k (PeerSecrets) delete k; }); }); } else { - clog(NetConnect) << "Disconnecting " << _h->socket->remote_endpoint() << " (Authentication Failed)"; + clog(NetConnect) << "Disconnecting " << socket->remote_endpoint() << " (Authentication Failed)"; boost::system::error_code ec; - _h->socket->shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); - _h->socket->close(); - delete _h; + socket->shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); + socket->close(); } } +//void Host::doHandshake(PeerHandshake* _h, boost::system::error_code _ech) +//{ +// if (_ech) +// { +// boost::system::error_code ec; +// _h->socket->shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); +// if (_h->socket->is_open()) +// _h->socket->close(); +// delete _h; +// return; +// } +// +// if (_h->isNew()) +// { +// clog(NetConnect) << "Authenticating connection for " << _h->socket->remote_endpoint(); +// +// if (_h->originated) +// { +// clog(NetConnect) << "devp2p.connect.egress sending auth"; +// // egress: tx auth +// asserts(_h->remote); +// _h->auth.resize(Signature::size + h256::size + Public::size + h256::size + 1); +// bytesConstRef sig(&_h->auth[0], Signature::size); +// bytesConstRef hepubk(&_h->auth[Signature::size], h256::size); +// bytesConstRef pubk(&_h->auth[Signature::size + h256::size], Public::size); +// bytesConstRef nonce(&_h->auth[Signature::size + h256::size + Public::size], h256::size); +// +// // E(remote-pubk, S(ecdhe-random, ecdh-shared-secret^nonce) || H(ecdhe-random-pubk) || pubk || nonce || 0x0) +// crypto::ecdh::agree(m_alias.sec(), _h->remote, _h->ss); +// sign(_h->ecdhe.seckey(), _h->ss ^ _h->nonce).ref().copyTo(sig); +// sha3(_h->ecdhe.pubkey().ref(), hepubk); +// m_alias.pub().ref().copyTo(pubk); +// _h->nonce.ref().copyTo(nonce); +// _h->auth[_h->auth.size() - 1] = 0x0; +// encrypt(_h->remote, &_h->auth, _h->authCipher); +// ba::async_write(*_h->socket, ba::buffer(_h->authCipher), [=](boost::system::error_code ec, std::size_t) +// { +// doHandshake(_h, ec); +// }); +// } +// else +// { +// clog(NetConnect) << "devp2p.connect.ingress recving auth"; +// // ingress: rx auth +// _h->authCipher.resize(279); +// ba::async_read(*_h->socket, ba::buffer(_h->authCipher, 279), [=](boost::system::error_code ec, std::size_t) +// { +// if (ec) +// doHandshake(_h, ec); +// else +// { +// decrypt(m_alias.sec(), bytesConstRef(&_h->authCipher), _h->auth); +// bytesConstRef sig(&_h->auth[0], Signature::size); +// bytesConstRef hepubk(&_h->auth[Signature::size], h256::size); +// bytesConstRef pubk(&_h->auth[Signature::size + h256::size], Public::size); +// bytesConstRef nonce(&_h->auth[Signature::size + h256::size + Public::size], h256::size); +// pubk.copyTo(_h->remote.ref()); +// nonce.copyTo(_h->remoteNonce.ref()); +// +// crypto::ecdh::agree(m_alias.sec(), _h->remote, _h->ss); +// _h->remoteEphemeral = recover(*(Signature*)sig.data(), _h->ss ^ _h->remoteNonce); +// assert(sha3(_h->remoteEphemeral) == *(h256*)hepubk.data()); +// doHandshake(_h); +// } +// }); +// } +// } +// else if (_h->isAcking()) +// if (_h->originated) +// { +// clog(NetConnect) << "devp2p.connect.egress recving ack"; +// // egress: rx ack +// _h->ackCipher.resize(182); +// ba::async_read(*_h->socket, ba::buffer(_h->ackCipher, 182), [=](boost::system::error_code ec, std::size_t) +// { +// if (ec) +// doHandshake(_h, ec); +// else +// { +// decrypt(m_alias.sec(), bytesConstRef(&_h->ackCipher), _h->ack); +// bytesConstRef(&_h->ack).cropped(0, Public::size).copyTo(_h->remoteEphemeral.ref()); +// bytesConstRef(&_h->ack).cropped(Public::size, h256::size).copyTo(_h->remoteNonce.ref()); +// doHandshake(_h); +// } +// }); +// } +// else +// { +// clog(NetConnect) << "devp2p.connect.ingress sending ack"; +// // ingress: tx ack +// _h->ack.resize(Public::size + h256::size + 1); +// bytesConstRef epubk(&_h->ack[0], Public::size); +// bytesConstRef nonce(&_h->ack[Public::size], h256::size); +// _h->ecdhe.pubkey().ref().copyTo(epubk); +// _h->nonce.ref().copyTo(nonce); +// _h->ack[_h->ack.size() - 1] = 0x0; +// encrypt(_h->remote, &_h->ack, _h->ackCipher); +// ba::async_write(*_h->socket, ba::buffer(_h->ackCipher), [=](boost::system::error_code ec, std::size_t) +// { +// doHandshake(_h, ec); +// }); +// } +// else if (_h->isAuthenticating()) +// { +// if (_h->originated) +// clog(NetConnect) << "devp2p.connect.egress sending magic sequence"; +// else +// clog(NetConnect) << "devp2p.connect.ingress sending magic sequence"; +// PeerSecrets* k = new PeerSecrets; +// bytes keyMaterialBytes(512); +// bytesConstRef keyMaterial(&keyMaterialBytes); +// +// _h->ecdhe.agree(_h->remoteEphemeral, _h->ess); +// _h->ess.ref().copyTo(keyMaterial.cropped(0, h256::size)); +// _h->ss.ref().copyTo(keyMaterial.cropped(h256::size, h256::size)); +// // auto token = sha3(ssA); +// k->encryptK = sha3(keyMaterial); +// k->encryptK.ref().copyTo(keyMaterial.cropped(h256::size, h256::size)); +// k->macK = sha3(keyMaterial); +// +// // Initiator egress-mac: sha3(mac-secret^recipient-nonce || auth-sent-init) +// // ingress-mac: sha3(mac-secret^initiator-nonce || auth-recvd-ack) +// // Recipient egress-mac: sha3(mac-secret^initiator-nonce || auth-sent-ack) +// // ingress-mac: sha3(mac-secret^recipient-nonce || auth-recvd-init) +// +// bytes const& egressCipher = _h->originated ? _h->authCipher : _h->ackCipher; +// keyMaterialBytes.resize(h256::size + egressCipher.size()); +// keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); +// (k->macK ^ _h->remoteNonce).ref().copyTo(keyMaterial); +// bytesConstRef(&egressCipher).copyTo(keyMaterial.cropped(h256::size, egressCipher.size())); +// k->egressMac = sha3(keyMaterial); +// +// bytes const& ingressCipher = _h->originated ? _h->ackCipher : _h->authCipher; +// keyMaterialBytes.resize(h256::size + ingressCipher.size()); +// keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); +// (k->macK ^ _h->nonce).ref().copyTo(keyMaterial); +// bytesConstRef(&ingressCipher).copyTo(keyMaterial.cropped(h256::size, ingressCipher.size())); +// k->ingressMac = sha3(keyMaterial); +// +// // TESTING: send encrypt magic sequence +// bytes magic {0x22,0x40,0x08,0x91}; +// encryptSymNoAuth(k->encryptK, &magic, k->magicCipherAndMac, h256()); +// k->magicCipherAndMac.resize(k->magicCipherAndMac.size() + 32); +// sha3mac(k->egressMac.ref(), &magic, k->egressMac.ref()); +// k->egressMac.ref().copyTo(bytesConstRef(&k->magicCipherAndMac).cropped(k->magicCipherAndMac.size() - 32, 32)); +// +// clog(NetConnect) << "devp2p.connect.egress txrx magic sequence"; +// k->recvdMagicCipherAndMac.resize(k->magicCipherAndMac.size()); +// +// ba::async_write(*_h->socket, ba::buffer(k->magicCipherAndMac), [this, k, _h, magic](boost::system::error_code ec, std::size_t) +// { +// if (ec) +// { +// delete k; +// doHandshake(_h, ec); +// return; +// } +// +// ba::async_read(*_h->socket, ba::buffer(k->recvdMagicCipherAndMac, k->magicCipherAndMac.size()), [this, k, _h, magic](boost::system::error_code ec, std::size_t) +// { +// if (_h->originated) +// clog(NetNote) << "devp2p.connect.egress recving magic sequence"; +// else +// clog(NetNote) << "devp2p.connect.ingress recving magic sequence"; +// +// if (ec) +// { +// delete k; +// doHandshake(_h, ec); +// return; +// } +// +// /// capabilities handshake (encrypted magic sequence is placeholder) +// bytes decryptedMagic; +// decryptSymNoAuth(k->encryptK, h256(), &k->recvdMagicCipherAndMac, decryptedMagic); +// if (decryptedMagic[0] == 0x22 && decryptedMagic[1] == 0x40 && decryptedMagic[2] == 0x08 && decryptedMagic[3] == 0x91) +// { +// shared_ptr p; +// p = m_peers[_h->remote]; +// +// if (!p) +// { +// p.reset(new Peer()); +// p->id = _h->remote; +// } +// p->endpoint.tcp.address(_h->socket->remote_endpoint().address()); +// p->m_lastDisconnect = NoDisconnect; +// p->m_lastConnected = std::chrono::system_clock::now(); +// p->m_failedAttempts = 0; +// +// auto ps = std::make_shared(this, move(*_h->socket), p); +// ps->start(); +// } +// +// delete k; +// }); +// }); +// } +// else +// { +// clog(NetConnect) << "Disconnecting " << _h->socket->remote_endpoint() << " (Authentication Failed)"; +// boost::system::error_code ec; +// _h->socket->shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); +// _h->socket->close(); +// delete _h; +// } +//} + string Host::pocHost() { vector strs; @@ -665,7 +877,8 @@ void Host::connect(std::shared_ptr const& _p) else { clog(NetConnect) << "Connected to" << _p->id.abridged() << "@" << _p->peerEndpoint(); - doHandshake(new Handshake(s, _p->id)); + auto handshake = make_shared(m_alias, s, _p->id); + handshake->start(); } Guard l(x_pendingNodeConns); m_pendingPeerConns.erase(nptr); diff --git a/libp2p/Host.h b/libp2p/Host.h index 3ca414c88..31ca22442 100644 --- a/libp2p/Host.h +++ b/libp2p/Host.h @@ -64,6 +64,80 @@ private: Host& m_host; }; +/** + * @brief Key material and derived secrets for TCP peer connection. + */ +struct PeerSecrets +{ + friend class PeerHandshake; +protected: + Secret encryptK; + Secret macK; + h256 egressMac; + h256 ingressMac; + + bytes magicCipherAndMac; + bytes recvdMagicCipherAndMac; +}; + +struct PeerHandshake: public std::enable_shared_from_this +{ + friend class Host; + enum State + { + New, // New->AckAuth [egress: tx auth, ingress: rx auth] + AckAuth, // AckAuth->Authenticating [egress: rx ack, ingress: tx ack] + Authenticating, // Authenticating [tx caps, rx caps, authenticate] + }; + + /// Handshake for ingress connection. Takes ownership of socket. + PeerHandshake(KeyPair const& _alias, bi::tcp::socket* _socket): alias(_alias), socket(std::move(_socket)), originated(false) { crypto::Nonce::get().ref().copyTo(nonce.ref()); } + + /// Handshake for egress connection to _remote. Takes ownership of socket. + PeerHandshake(KeyPair const& _alias, bi::tcp::socket* _socket, NodeId _remote): alias(_alias), socket(std::move(_socket)), originated(true), remote(_remote) { crypto::Nonce::get().ref().copyTo(nonce.ref()); } + + ~PeerHandshake() { delete socket; } + +protected: + /// Returns true when the auth message is to be sent or received. + bool isNew() { return state == New; } + + /// Returns true when the ack message is to be sent or received. + bool isAcking() { return state == AckAuth; } + + /// Returns true when auth and ack messages have been received and caps message is to be sent, received, and authenticated. + bool isAuthenticating() { return state == Authenticating; } + + void start() { transition(); } + +private: + void transition(boost::system::error_code _ech = boost::system::error_code()); + + /// Current state of handshake. + State state = New; + + KeyPair const& alias; + + /// Node id of remote host for socket. + NodeId remote; + + bi::tcp::socket* socket; + bool originated = false; + + bytes auth; + bytes authCipher; + bytes ack; + bytes ackCipher; + Secret ss; + Secret ess; + + crypto::ECDHE ecdhe; + h256 nonce; + + Public remoteEphemeral; + h256 remoteNonce; +}; + /** * @brief The Host class * Capabilities should be registered prior to startNetwork, since m_capabilities is not thread-safe. @@ -158,48 +232,6 @@ protected: void restoreNetwork(bytesConstRef _b); private: - struct Handshake - { - /// Handshake for ingress connection. Takes ownership of socket. - Handshake(bi::tcp::socket* _socket): socket(std::move(_socket)), originated(false) { crypto::Nonce::get().ref().copyTo(nonce.ref()); } - - /// Handshake for egress connection to _remote. Takes ownership of socket. - Handshake(bi::tcp::socket* _socket, NodeId _remote): socket(std::move(_socket)), originated(true), remote(_remote) { crypto::Nonce::get().ref().copyTo(nonce.ref()); } - - ~Handshake() { delete socket; } - - bool started() { return auth.size() > 0; } - bool acked() { return ack.size() > 0; } - - /// If originated this is accepting (ingress) node id, otherwise it is originating (egress) node. - NodeId remote; - bi::tcp::socket *socket; - bool originated = false; - - bytes auth; - bytes authCipher; - bytes ack; - bytes ackCipher; - Secret ss; - Secret ess; - - crypto::ECDHE ecdhe; - h256 nonce; - - Public remoteEphemeral; - h256 remoteNonce; - }; - struct PeerSecrets - { - Secret encryptK; - Secret macK; - h256 egressMac; - h256 ingressMac; - - bytes magicCipherAndMac; - bytes recvdMagicCipherAndMac; - }; - /// Populate m_peerAddresses with available public addresses. void determinePublic(std::string const& _publicAddress, bool _upnp); @@ -213,10 +245,7 @@ private: /// Called only from startedWorking(). void runAcceptor(); - - /// Attempt to authenticate peer and establish a new session. - void doHandshake(Handshake* _h, boost::system::error_code _ec = boost::system::error_code()); - + void seal(bytes& _b); /// Called by Worker. Not thread-safe; to be called only by worker. diff --git a/libp2p/Peer.h b/libp2p/Peer.h index 704e5c2b4..5c1aa015d 100644 --- a/libp2p/Peer.h +++ b/libp2p/Peer.h @@ -29,7 +29,7 @@ namespace dev namespace p2p { - + /** * @brief Representation of connectivity state and all other pertinent Peer metadata. * A Peer represents connectivity between two nodes, which in this case, are the host @@ -54,6 +54,7 @@ class Peer: public Node { friend class Session; /// Allows Session to update score and rating. friend class Host; /// For Host: saveNetwork(), restoreNetwork() + friend struct PeerHandshake; public: bool isOffline() const { return !m_session.lock(); } From 4ebc2f8320c1f8f045b384712a771a7bc51bb4c6 Mon Sep 17 00:00:00 2001 From: subtly Date: Wed, 18 Feb 2015 00:56:52 -0500 Subject: [PATCH 011/100] reenable sessions. more refactor. --- libp2p/Host.cpp | 23 ++++---- libp2p/Host.h | 153 ++++++++++++++++++++++++------------------------ 2 files changed, 86 insertions(+), 90 deletions(-) diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index 1b6d8d65b..d79b6bd39 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -338,7 +338,7 @@ void Host::runAcceptor() { // doHandshake takes ownersihp of *s via std::move // incoming connection; we don't yet know nodeid - auto handshake = make_shared(m_alias, s); + auto handshake = make_shared(this, s); handshake->start(); success = true; } @@ -399,10 +399,10 @@ void PeerHandshake::transition(boost::system::error_code _ech) { bytesConstRef nonce(&auth[Signature::size + h256::size + Public::size], h256::size); // E(remote-pubk, S(ecdhe-random, ecdh-shared-secret^nonce) || H(ecdhe-random-pubk) || pubk || nonce || 0x0) - crypto::ecdh::agree(alias.sec(), remote, ss); + crypto::ecdh::agree(host->m_alias.sec(), remote, ss); sign(ecdhe.seckey(), ss ^ this->nonce).ref().copyTo(sig); sha3(ecdhe.pubkey().ref(), hepubk); - alias.pub().ref().copyTo(pubk); + host->m_alias.pub().ref().copyTo(pubk); this->nonce.ref().copyTo(nonce); auth[auth.size() - 1] = 0x0; encrypt(remote, &auth, authCipher); @@ -423,7 +423,7 @@ void PeerHandshake::transition(boost::system::error_code _ech) { transition(ec); else { - decrypt(alias.sec(), bytesConstRef(&authCipher), auth); + decrypt(host->m_alias.sec(), bytesConstRef(&authCipher), auth); bytesConstRef sig(&auth[0], Signature::size); bytesConstRef hepubk(&auth[Signature::size], h256::size); bytesConstRef pubk(&auth[Signature::size + h256::size], Public::size); @@ -431,7 +431,7 @@ void PeerHandshake::transition(boost::system::error_code _ech) { pubk.copyTo(remote.ref()); nonce.copyTo(remoteNonce.ref()); - crypto::ecdh::agree(alias.sec(), remote, ss); + crypto::ecdh::agree(host->m_alias.sec(), remote, ss); remoteEphemeral = recover(*(Signature*)sig.data(), ss ^ remoteNonce); assert(sha3(remoteEphemeral) == *(h256*)hepubk.data()); transition(); @@ -451,7 +451,7 @@ void PeerHandshake::transition(boost::system::error_code _ech) { transition(ec); else { - decrypt(alias.sec(), bytesConstRef(&ackCipher), ack); + decrypt(host->m_alias.sec(), bytesConstRef(&ackCipher), ack); bytesConstRef(&ack).cropped(0, Public::size).copyTo(remoteEphemeral.ref()); bytesConstRef(&ack).cropped(Public::size, h256::size).copyTo(remoteNonce.ref()); transition(); @@ -551,7 +551,7 @@ void PeerHandshake::transition(boost::system::error_code _ech) { { shared_ptr p; // todo: need host -// p = m_peers[remote]; + p = host->m_peers[remote]; if (!p) { @@ -562,10 +562,9 @@ void PeerHandshake::transition(boost::system::error_code _ech) { p->m_lastDisconnect = NoDisconnect; p->m_lastConnected = std::chrono::system_clock::now(); p->m_failedAttempts = 0; - - // todo: need host -// auto ps = std::make_shared(this, move(*socket), p); -// ps->start(); + + auto ps = std::make_shared(host, move(*socket), p); + ps->start(); } // todo: PeerSession needs to take ownership of k (PeerSecrets) @@ -877,7 +876,7 @@ void Host::connect(std::shared_ptr const& _p) else { clog(NetConnect) << "Connected to" << _p->id.abridged() << "@" << _p->peerEndpoint(); - auto handshake = make_shared(m_alias, s, _p->id); + auto handshake = make_shared(this, s, _p->id); handshake->start(); } Guard l(x_pendingNodeConns); diff --git a/libp2p/Host.h b/libp2p/Host.h index 31ca22442..9a49d2706 100644 --- a/libp2p/Host.h +++ b/libp2p/Host.h @@ -63,98 +63,21 @@ private: Host& m_host; }; - -/** - * @brief Key material and derived secrets for TCP peer connection. - */ -struct PeerSecrets -{ - friend class PeerHandshake; -protected: - Secret encryptK; - Secret macK; - h256 egressMac; - h256 ingressMac; - - bytes magicCipherAndMac; - bytes recvdMagicCipherAndMac; -}; - -struct PeerHandshake: public std::enable_shared_from_this -{ - friend class Host; - enum State - { - New, // New->AckAuth [egress: tx auth, ingress: rx auth] - AckAuth, // AckAuth->Authenticating [egress: rx ack, ingress: tx ack] - Authenticating, // Authenticating [tx caps, rx caps, authenticate] - }; - - /// Handshake for ingress connection. Takes ownership of socket. - PeerHandshake(KeyPair const& _alias, bi::tcp::socket* _socket): alias(_alias), socket(std::move(_socket)), originated(false) { crypto::Nonce::get().ref().copyTo(nonce.ref()); } - - /// Handshake for egress connection to _remote. Takes ownership of socket. - PeerHandshake(KeyPair const& _alias, bi::tcp::socket* _socket, NodeId _remote): alias(_alias), socket(std::move(_socket)), originated(true), remote(_remote) { crypto::Nonce::get().ref().copyTo(nonce.ref()); } - - ~PeerHandshake() { delete socket; } - -protected: - /// Returns true when the auth message is to be sent or received. - bool isNew() { return state == New; } - - /// Returns true when the ack message is to be sent or received. - bool isAcking() { return state == AckAuth; } - - /// Returns true when auth and ack messages have been received and caps message is to be sent, received, and authenticated. - bool isAuthenticating() { return state == Authenticating; } - - void start() { transition(); } - -private: - void transition(boost::system::error_code _ech = boost::system::error_code()); - - /// Current state of handshake. - State state = New; - - KeyPair const& alias; - - /// Node id of remote host for socket. - NodeId remote; - - bi::tcp::socket* socket; - bool originated = false; - - bytes auth; - bytes authCipher; - bytes ack; - bytes ackCipher; - Secret ss; - Secret ess; - - crypto::ECDHE ecdhe; - h256 nonce; - - Public remoteEphemeral; - h256 remoteNonce; -}; /** * @brief The Host class * Capabilities should be registered prior to startNetwork, since m_capabilities is not thread-safe. * - * @todo exceptions when nodeTable not set (prior to start) - * @todo onNodeTableEvent: move peer-connection logic into ensurePeers * @todo handshake: gracefully disconnect peer if peer already connected * @todo abstract socket -> IPConnection * @todo determinePublic: ipv6, udp * @todo handle conflict if addNode/requireNode called and Node already exists w/conflicting tcp or udp port - * @todo write host identifier to disk w/nodes * @todo per-session keepalive/ping instead of broadcast; set ping-timeout via median-latency - * @todo configuration-management (NetworkPrefs+Keys+Topology) */ class Host: public Worker { friend class HostNodeTableHandler; + friend struct PeerHandshake; friend class Session; friend class HostCapabilityFace; @@ -308,5 +231,79 @@ private: bool m_accepting = false; }; +/** + * @brief Key material and derived secrets for TCP peer connection. + */ +struct PeerSecrets +{ + friend struct PeerHandshake; +protected: + Secret encryptK; + Secret macK; + h256 egressMac; + h256 ingressMac; + + bytes magicCipherAndMac; + bytes recvdMagicCipherAndMac; +}; + +struct PeerHandshake: public std::enable_shared_from_this +{ + friend class Host; + enum State + { + New, // New->AckAuth [egress: tx auth, ingress: rx auth] + AckAuth, // AckAuth->Authenticating [egress: rx ack, ingress: tx ack] + Authenticating, // Authenticating [tx caps, rx caps, authenticate] + }; + + /// Handshake for ingress connection. Takes ownership of socket. + PeerHandshake(Host* _host, bi::tcp::socket* _socket): host(_host), socket(std::move(_socket)), originated(false) { crypto::Nonce::get().ref().copyTo(nonce.ref()); } + + /// Handshake for egress connection to _remote. Takes ownership of socket. + PeerHandshake(Host* _host, bi::tcp::socket* _socket, NodeId _remote): host(_host), socket(std::move(_socket)), originated(true), remote(_remote) { crypto::Nonce::get().ref().copyTo(nonce.ref()); } + + ~PeerHandshake() { delete socket; } + +protected: + /// Returns true when the auth message is to be sent or received. + bool isNew() { return state == New; } + + /// Returns true when the ack message is to be sent or received. + bool isAcking() { return state == AckAuth; } + + /// Returns true when auth and ack messages have been received and caps message is to be sent, received, and authenticated. + bool isAuthenticating() { return state == Authenticating; } + + void start() { transition(); } + +private: + void transition(boost::system::error_code _ech = boost::system::error_code()); + + /// Current state of handshake. + State state = New; + + Host* host; + + /// Node id of remote host for socket. + NodeId remote; + + bi::tcp::socket* socket; + bool originated = false; + + bytes auth; + bytes authCipher; + bytes ack; + bytes ackCipher; + Secret ss; + Secret ess; + + crypto::ECDHE ecdhe; + h256 nonce; + + Public remoteEphemeral; + h256 remoteNonce; +}; + } } From 8e8b6de2558f9001c51b2644e2191adf725cf2b5 Mon Sep 17 00:00:00 2001 From: subtly Date: Wed, 18 Feb 2015 01:03:18 -0500 Subject: [PATCH 012/100] advance transition --- libp2p/Host.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index d79b6bd39..4d3998f95 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -385,6 +385,8 @@ void PeerHandshake::transition(boost::system::error_code _ech) { auto self(shared_from_this()); if (isNew()) { + state = AckAuth; + clog(NetConnect) << "Authenticating connection for " << socket->remote_endpoint(); if (originated) @@ -440,6 +442,9 @@ void PeerHandshake::transition(boost::system::error_code _ech) { } } else if (isAcking()) + { + state = Authenticating; + if (originated) { clog(NetConnect) << "devp2p.connect.egress recving ack"; @@ -474,6 +479,7 @@ void PeerHandshake::transition(boost::system::error_code _ech) { transition(ec); }); } + } else if (isAuthenticating()) { if (originated) From f6fc86e992cc37699e2ce48cb91f0b6435256663 Mon Sep 17 00:00:00 2001 From: subtly Date: Wed, 18 Feb 2015 01:21:49 -0500 Subject: [PATCH 013/100] fix curlies. remove unused transition methods. --- libp2p/Host.cpp | 226 ++---------------------------------------------- libp2p/Host.h | 11 +-- 2 files changed, 10 insertions(+), 227 deletions(-) diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index 4d3998f95..73290568a 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -372,7 +372,8 @@ void Host::runAcceptor() } } -void PeerHandshake::transition(boost::system::error_code _ech) { +void PeerHandshake::transition(boost::system::error_code _ech) +{ if (_ech) { boost::system::error_code ec; @@ -383,9 +384,9 @@ void PeerHandshake::transition(boost::system::error_code _ech) { } auto self(shared_from_this()); - if (isNew()) + if (nextState == New) { - state = AckAuth; + nextState = AckAuth; clog(NetConnect) << "Authenticating connection for " << socket->remote_endpoint(); @@ -441,9 +442,9 @@ void PeerHandshake::transition(boost::system::error_code _ech) { }); } } - else if (isAcking()) + else if (nextState == AckAuth) { - state = Authenticating; + nextState = Authenticating; if (originated) { @@ -480,7 +481,7 @@ void PeerHandshake::transition(boost::system::error_code _ech) { }); } } - else if (isAuthenticating()) + else if (nextState == Authenticating) { if (originated) clog(NetConnect) << "devp2p.connect.egress sending magic sequence"; @@ -517,6 +518,7 @@ void PeerHandshake::transition(boost::system::error_code _ech) { bytesConstRef(&ingressCipher).copyTo(keyMaterial.cropped(h256::size, ingressCipher.size())); k->ingressMac = sha3(keyMaterial); + // This test will be replaced with protocol-capabilities information (was Hello packet) // TESTING: send encrypt magic sequence bytes magic {0x22,0x40,0x08,0x91}; encryptSymNoAuth(k->encryptK, &magic, k->magicCipherAndMac, h256()); @@ -556,9 +558,7 @@ void PeerHandshake::transition(boost::system::error_code _ech) { if (decryptedMagic[0] == 0x22 && decryptedMagic[1] == 0x40 && decryptedMagic[2] == 0x08 && decryptedMagic[3] == 0x91) { shared_ptr p; - // todo: need host p = host->m_peers[remote]; - if (!p) { p.reset(new Peer()); @@ -573,7 +573,7 @@ void PeerHandshake::transition(boost::system::error_code _ech) { ps->start(); } - // todo: PeerSession needs to take ownership of k (PeerSecrets) + // todo: PeerSession will take ownership of k and use it to encrypt wireline. delete k; }); }); @@ -587,214 +587,6 @@ void PeerHandshake::transition(boost::system::error_code _ech) { } } -//void Host::doHandshake(PeerHandshake* _h, boost::system::error_code _ech) -//{ -// if (_ech) -// { -// boost::system::error_code ec; -// _h->socket->shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); -// if (_h->socket->is_open()) -// _h->socket->close(); -// delete _h; -// return; -// } -// -// if (_h->isNew()) -// { -// clog(NetConnect) << "Authenticating connection for " << _h->socket->remote_endpoint(); -// -// if (_h->originated) -// { -// clog(NetConnect) << "devp2p.connect.egress sending auth"; -// // egress: tx auth -// asserts(_h->remote); -// _h->auth.resize(Signature::size + h256::size + Public::size + h256::size + 1); -// bytesConstRef sig(&_h->auth[0], Signature::size); -// bytesConstRef hepubk(&_h->auth[Signature::size], h256::size); -// bytesConstRef pubk(&_h->auth[Signature::size + h256::size], Public::size); -// bytesConstRef nonce(&_h->auth[Signature::size + h256::size + Public::size], h256::size); -// -// // E(remote-pubk, S(ecdhe-random, ecdh-shared-secret^nonce) || H(ecdhe-random-pubk) || pubk || nonce || 0x0) -// crypto::ecdh::agree(m_alias.sec(), _h->remote, _h->ss); -// sign(_h->ecdhe.seckey(), _h->ss ^ _h->nonce).ref().copyTo(sig); -// sha3(_h->ecdhe.pubkey().ref(), hepubk); -// m_alias.pub().ref().copyTo(pubk); -// _h->nonce.ref().copyTo(nonce); -// _h->auth[_h->auth.size() - 1] = 0x0; -// encrypt(_h->remote, &_h->auth, _h->authCipher); -// ba::async_write(*_h->socket, ba::buffer(_h->authCipher), [=](boost::system::error_code ec, std::size_t) -// { -// doHandshake(_h, ec); -// }); -// } -// else -// { -// clog(NetConnect) << "devp2p.connect.ingress recving auth"; -// // ingress: rx auth -// _h->authCipher.resize(279); -// ba::async_read(*_h->socket, ba::buffer(_h->authCipher, 279), [=](boost::system::error_code ec, std::size_t) -// { -// if (ec) -// doHandshake(_h, ec); -// else -// { -// decrypt(m_alias.sec(), bytesConstRef(&_h->authCipher), _h->auth); -// bytesConstRef sig(&_h->auth[0], Signature::size); -// bytesConstRef hepubk(&_h->auth[Signature::size], h256::size); -// bytesConstRef pubk(&_h->auth[Signature::size + h256::size], Public::size); -// bytesConstRef nonce(&_h->auth[Signature::size + h256::size + Public::size], h256::size); -// pubk.copyTo(_h->remote.ref()); -// nonce.copyTo(_h->remoteNonce.ref()); -// -// crypto::ecdh::agree(m_alias.sec(), _h->remote, _h->ss); -// _h->remoteEphemeral = recover(*(Signature*)sig.data(), _h->ss ^ _h->remoteNonce); -// assert(sha3(_h->remoteEphemeral) == *(h256*)hepubk.data()); -// doHandshake(_h); -// } -// }); -// } -// } -// else if (_h->isAcking()) -// if (_h->originated) -// { -// clog(NetConnect) << "devp2p.connect.egress recving ack"; -// // egress: rx ack -// _h->ackCipher.resize(182); -// ba::async_read(*_h->socket, ba::buffer(_h->ackCipher, 182), [=](boost::system::error_code ec, std::size_t) -// { -// if (ec) -// doHandshake(_h, ec); -// else -// { -// decrypt(m_alias.sec(), bytesConstRef(&_h->ackCipher), _h->ack); -// bytesConstRef(&_h->ack).cropped(0, Public::size).copyTo(_h->remoteEphemeral.ref()); -// bytesConstRef(&_h->ack).cropped(Public::size, h256::size).copyTo(_h->remoteNonce.ref()); -// doHandshake(_h); -// } -// }); -// } -// else -// { -// clog(NetConnect) << "devp2p.connect.ingress sending ack"; -// // ingress: tx ack -// _h->ack.resize(Public::size + h256::size + 1); -// bytesConstRef epubk(&_h->ack[0], Public::size); -// bytesConstRef nonce(&_h->ack[Public::size], h256::size); -// _h->ecdhe.pubkey().ref().copyTo(epubk); -// _h->nonce.ref().copyTo(nonce); -// _h->ack[_h->ack.size() - 1] = 0x0; -// encrypt(_h->remote, &_h->ack, _h->ackCipher); -// ba::async_write(*_h->socket, ba::buffer(_h->ackCipher), [=](boost::system::error_code ec, std::size_t) -// { -// doHandshake(_h, ec); -// }); -// } -// else if (_h->isAuthenticating()) -// { -// if (_h->originated) -// clog(NetConnect) << "devp2p.connect.egress sending magic sequence"; -// else -// clog(NetConnect) << "devp2p.connect.ingress sending magic sequence"; -// PeerSecrets* k = new PeerSecrets; -// bytes keyMaterialBytes(512); -// bytesConstRef keyMaterial(&keyMaterialBytes); -// -// _h->ecdhe.agree(_h->remoteEphemeral, _h->ess); -// _h->ess.ref().copyTo(keyMaterial.cropped(0, h256::size)); -// _h->ss.ref().copyTo(keyMaterial.cropped(h256::size, h256::size)); -// // auto token = sha3(ssA); -// k->encryptK = sha3(keyMaterial); -// k->encryptK.ref().copyTo(keyMaterial.cropped(h256::size, h256::size)); -// k->macK = sha3(keyMaterial); -// -// // Initiator egress-mac: sha3(mac-secret^recipient-nonce || auth-sent-init) -// // ingress-mac: sha3(mac-secret^initiator-nonce || auth-recvd-ack) -// // Recipient egress-mac: sha3(mac-secret^initiator-nonce || auth-sent-ack) -// // ingress-mac: sha3(mac-secret^recipient-nonce || auth-recvd-init) -// -// bytes const& egressCipher = _h->originated ? _h->authCipher : _h->ackCipher; -// keyMaterialBytes.resize(h256::size + egressCipher.size()); -// keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); -// (k->macK ^ _h->remoteNonce).ref().copyTo(keyMaterial); -// bytesConstRef(&egressCipher).copyTo(keyMaterial.cropped(h256::size, egressCipher.size())); -// k->egressMac = sha3(keyMaterial); -// -// bytes const& ingressCipher = _h->originated ? _h->ackCipher : _h->authCipher; -// keyMaterialBytes.resize(h256::size + ingressCipher.size()); -// keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); -// (k->macK ^ _h->nonce).ref().copyTo(keyMaterial); -// bytesConstRef(&ingressCipher).copyTo(keyMaterial.cropped(h256::size, ingressCipher.size())); -// k->ingressMac = sha3(keyMaterial); -// -// // TESTING: send encrypt magic sequence -// bytes magic {0x22,0x40,0x08,0x91}; -// encryptSymNoAuth(k->encryptK, &magic, k->magicCipherAndMac, h256()); -// k->magicCipherAndMac.resize(k->magicCipherAndMac.size() + 32); -// sha3mac(k->egressMac.ref(), &magic, k->egressMac.ref()); -// k->egressMac.ref().copyTo(bytesConstRef(&k->magicCipherAndMac).cropped(k->magicCipherAndMac.size() - 32, 32)); -// -// clog(NetConnect) << "devp2p.connect.egress txrx magic sequence"; -// k->recvdMagicCipherAndMac.resize(k->magicCipherAndMac.size()); -// -// ba::async_write(*_h->socket, ba::buffer(k->magicCipherAndMac), [this, k, _h, magic](boost::system::error_code ec, std::size_t) -// { -// if (ec) -// { -// delete k; -// doHandshake(_h, ec); -// return; -// } -// -// ba::async_read(*_h->socket, ba::buffer(k->recvdMagicCipherAndMac, k->magicCipherAndMac.size()), [this, k, _h, magic](boost::system::error_code ec, std::size_t) -// { -// if (_h->originated) -// clog(NetNote) << "devp2p.connect.egress recving magic sequence"; -// else -// clog(NetNote) << "devp2p.connect.ingress recving magic sequence"; -// -// if (ec) -// { -// delete k; -// doHandshake(_h, ec); -// return; -// } -// -// /// capabilities handshake (encrypted magic sequence is placeholder) -// bytes decryptedMagic; -// decryptSymNoAuth(k->encryptK, h256(), &k->recvdMagicCipherAndMac, decryptedMagic); -// if (decryptedMagic[0] == 0x22 && decryptedMagic[1] == 0x40 && decryptedMagic[2] == 0x08 && decryptedMagic[3] == 0x91) -// { -// shared_ptr p; -// p = m_peers[_h->remote]; -// -// if (!p) -// { -// p.reset(new Peer()); -// p->id = _h->remote; -// } -// p->endpoint.tcp.address(_h->socket->remote_endpoint().address()); -// p->m_lastDisconnect = NoDisconnect; -// p->m_lastConnected = std::chrono::system_clock::now(); -// p->m_failedAttempts = 0; -// -// auto ps = std::make_shared(this, move(*_h->socket), p); -// ps->start(); -// } -// -// delete k; -// }); -// }); -// } -// else -// { -// clog(NetConnect) << "Disconnecting " << _h->socket->remote_endpoint() << " (Authentication Failed)"; -// boost::system::error_code ec; -// _h->socket->shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); -// _h->socket->close(); -// delete _h; -// } -//} - string Host::pocHost() { vector strs; diff --git a/libp2p/Host.h b/libp2p/Host.h index 9a49d2706..81016333a 100644 --- a/libp2p/Host.h +++ b/libp2p/Host.h @@ -266,22 +266,13 @@ struct PeerHandshake: public std::enable_shared_from_this ~PeerHandshake() { delete socket; } protected: - /// Returns true when the auth message is to be sent or received. - bool isNew() { return state == New; } - - /// Returns true when the ack message is to be sent or received. - bool isAcking() { return state == AckAuth; } - - /// Returns true when auth and ack messages have been received and caps message is to be sent, received, and authenticated. - bool isAuthenticating() { return state == Authenticating; } - void start() { transition(); } private: void transition(boost::system::error_code _ech = boost::system::error_code()); /// Current state of handshake. - State state = New; + State nextState = New; Host* host; From 953bfd240057f231ac5d7ed57d06fe2e8ad7f282 Mon Sep 17 00:00:00 2001 From: subtly Date: Sun, 1 Mar 2015 22:17:17 +0100 Subject: [PATCH 014/100] start of ecies interop with go --- libdevcrypto/Common.cpp | 37 +++++++------ libdevcrypto/Common.h | 6 +-- libdevcrypto/CryptoPP.cpp | 110 +++++++++++++++++++++++++++++++++++++- libdevcrypto/CryptoPP.h | 9 ++++ libp2p/Host.cpp | 22 ++++---- test/crypto.cpp | 78 ++++++++++++++++++++++----- 6 files changed, 214 insertions(+), 48 deletions(-) diff --git a/libdevcrypto/Common.cpp b/libdevcrypto/Common.cpp index 21b854ca3..7e3c403cd 100644 --- a/libdevcrypto/Common.cpp +++ b/libdevcrypto/Common.cpp @@ -94,24 +94,23 @@ bool dev::decryptSym(Secret const& _k, bytesConstRef _cipher, bytes& o_plain) return decrypt(_k, _cipher, o_plain); } -h256 dev::encryptSymNoAuth(Secret const& _k, bytesConstRef _plain, bytes& o_cipher) +h128 dev::encryptSymNoAuth(Secret const& _k, bytesConstRef _plain, bytes& o_cipher) { - auto iv = Nonce::get(); + h128 iv(Nonce::get()); return encryptSymNoAuth(_k, _plain, o_cipher, iv); } -h256 dev::encryptSymNoAuth(Secret const& _k, bytesConstRef _plain, bytes& o_cipher, h256 const& _iv) +h128 dev::encryptSymNoAuth(Secret const& _k, bytesConstRef _plain, bytes& o_cipher, h128 const& _iv) { const int c_aesBlockLen = 16; - o_cipher.resize(_plain.size() + (c_aesBlockLen - (_plain.size() % c_aesBlockLen)) % c_aesBlockLen); + size_t extraBytes = _plain.size() % c_aesBlockLen; + size_t trimmedSize = _plain.size() - extraBytes; + size_t paddedSize = _plain.size() + ((16 - extraBytes) % 16); + o_cipher.resize(paddedSize); - bytes underflowBytes(0); - auto size = _plain.size() - (_plain.size() % c_aesBlockLen); - if (o_cipher.size() > _plain.size()) - { - underflowBytes.resize(c_aesBlockLen); - _plain.cropped(size, _plain.size() - size).copyTo(&underflowBytes); - } + bytes underflowBytes(16); + if (o_cipher.size() != _plain.size()) + _plain.cropped(trimmedSize, extraBytes).copyTo(&underflowBytes); const int c_aesKeyLen = 32; SecByteBlock key(_k.data(), c_aesKeyLen); @@ -119,28 +118,28 @@ h256 dev::encryptSymNoAuth(Secret const& _k, bytesConstRef _plain, bytes& o_ciph { CTR_Mode::Encryption e; e.SetKeyWithIV(key, key.size(), _iv.data()); - if (size) - e.ProcessData(o_cipher.data(), _plain.data(), _plain.size()); - if (underflowBytes.size()) - e.ProcessData(o_cipher.data(), underflowBytes.data(), underflowBytes.size()); + if (trimmedSize) + e.ProcessData(o_cipher.data(), _plain.data(), trimmedSize); + if (extraBytes) + e.ProcessData(o_cipher.data() + trimmedSize, underflowBytes.data(), underflowBytes.size()); return _iv; } catch(CryptoPP::Exception& e) { cerr << e.what() << endl; o_cipher.resize(0); - return h256(); + return h128(); } } -bool dev::decryptSymNoAuth(Secret const& _k, h256 const& _iv, bytesConstRef _cipher, bytes& o_plaintext) +bool dev::decryptSymNoAuth(Secret const& _k, h128 const& _iv, bytesConstRef _cipher, bytes& o_plaintext) { - const int c_aesKeyLen = 32; const int c_aesBlockLen = 16; asserts(_cipher.size() % c_aesBlockLen == 0); o_plaintext.resize(_cipher.size()); - SecByteBlock key(_k.data(), c_aesKeyLen); + const int c_aesKeyLen = 32; + SecByteBlock key(_k.data(), c_aesKeyLen); try { CTR_Mode::Decryption d; diff --git a/libdevcrypto/Common.h b/libdevcrypto/Common.h index 9c33c4bb3..92a13f9a5 100644 --- a/libdevcrypto/Common.h +++ b/libdevcrypto/Common.h @@ -96,9 +96,9 @@ void encryptSym(Secret const& _k, bytesConstRef _plain, bytes& o_cipher); /// Symmetric decryption. bool decryptSym(Secret const& _k, bytesConstRef _cipher, bytes& o_plaintext); -h256 encryptSymNoAuth(Secret const& _k, bytesConstRef _plain, bytes& o_cipher); -h256 encryptSymNoAuth(Secret const& _k, bytesConstRef _plain, bytes& o_cipher, h256 const& _iv); -bool decryptSymNoAuth(Secret const& _k, h256 const& _iv, bytesConstRef _cipher, bytes& o_plaintext); +h128 encryptSymNoAuth(Secret const& _k, bytesConstRef _plain, bytes& o_cipher); +h128 encryptSymNoAuth(Secret const& _k, bytesConstRef _plain, bytes& o_cipher, h128 const& _iv); +bool decryptSymNoAuth(Secret const& _k, h128 const& _iv, bytesConstRef _cipher, bytes& o_plaintext); /// Recovers Public key from signed message hash. Public recover(Signature const& _sig, h256 const& _hash); diff --git a/libdevcrypto/CryptoPP.cpp b/libdevcrypto/CryptoPP.cpp index d9ccb3a35..47061d6d0 100644 --- a/libdevcrypto/CryptoPP.cpp +++ b/libdevcrypto/CryptoPP.cpp @@ -19,8 +19,9 @@ * @date 2014 */ -#include "CryptoPP.h" #include +#include "ECDHE.h" +#include "CryptoPP.h" using namespace std; using namespace dev; @@ -31,6 +32,113 @@ static_assert(dev::Secret::size == 32, "Secret key must be 32 bytes."); static_assert(dev::Public::size == 64, "Public key must be 64 bytes."); static_assert(dev::Signature::size == 65, "Signature must be 65 bytes."); +bytes Secp256k1::eciesKDF(Secret _z, bytes _s1, unsigned kdBitLen) +{ + // similar to go ecies implementation + + if (!_s1.size()) + { + _s1.resize(1); + asserts(_s1[0] == 0); + } + + // for sha3, hash.blocksize is 1088 bits, but this might really be digest size + auto reps = ((kdBitLen + 7) * 8) / (32 * 8); + bytes ctr({0, 0, 0, 1}); + bytes k; + CryptoPP::SHA256 ctx; + while (reps--) + { + ctx.Update(ctr.data(), ctr.size()); + ctx.Update(_z.data(), Secret::size); + ctx.Update(_s1.data(), _s1.size()); + // append hash to k + bytes digest(32); + ctx.Final(digest.data()); + ctx.Restart(); + + k.reserve(k.size() + h256::size); + move(digest.begin(), digest.end(), back_inserter(k)); + + if (ctr[3]++ && ctr[3] != 0) { + continue; + } else if (ctr[2]++ && ctr[2] != 0) { + continue; + } else if (ctr[1]++ && ctr[1] != 0) { + continue; + } else + ctr[0]++; + } + + k.resize(kdBitLen / 8); + return move(k); +} + +void Secp256k1::encryptECIES(Public const& _k, bytes& io_cipher) +{ + // similar to go ecies implementation + // todo: s1/s2/tag + + auto r = KeyPair::create(); + h256 z; + ecdh::agree(r.sec(), _k, z); + auto key = eciesKDF(z, bytes(), 512); + bytesConstRef eKey = bytesConstRef(&key).cropped(0, 32); + bytesRef mKey = bytesRef(&key).cropped(32, 32); + sha3(mKey, mKey); + + bytes cipherText; + encryptSymNoAuth(*(Secret*)eKey.data(), bytesConstRef(&io_cipher), cipherText, h128()); + if (!cipherText.size()) + return; + +// d := messageTag(params.Hash, Km, em, s2) +// copy(ct[len(Rb)+len(em):], d) + + bytes msg(1 + Public::size + cipherText.size() + 32); + msg[0] = 0x04; + r.pub().ref().copyTo(bytesRef(&msg).cropped(1, Public::size)); + bytesRef msgCipherRef = bytesRef(&msg).cropped(1 + Public::size, cipherText.size()); + bytesConstRef(&cipherText).copyTo(msgCipherRef); + + io_cipher.resize(msg.size()); + io_cipher.swap(msg); +} + +void eciesMessageTag() +{ + +} + +void Secp256k1::decryptECIES(Secret const& _k, bytes& io_text) +{ + // similar to go ecies implementation + // todo: s1/s2 + + // io_cipher[0] must be 2, 3, or 4, else invalidpublickey + if (io_text[0] < 2 || io_text[0] > 4) + // invalid message: publickey + return; + + if (io_text.size() < (1 + Public::size + h256::size + 1)) + // invalid message: length + return; + + h256 z; + ecdh::agree(_k, *(Public*)(io_text.data()+1), z); + auto key = eciesKDF(z, bytes(), 512); + bytesConstRef eKey = bytesConstRef(&key).cropped(0, 32); + bytesRef mKey = bytesRef(&key).cropped(32, 32); + sha3(mKey, mKey); + + bytes plain; + size_t cipherLen = io_text.size() - 1 - Public::size - h256::size; + bytesConstRef cipher(io_text.data() + 1 + Public::size, cipherLen); + decryptSymNoAuth(*(Secret*)eKey.data(), h128(), cipher, plain); + io_text.resize(plain.size()); + io_text.swap(plain); +} + void Secp256k1::encrypt(Public const& _k, bytes& io_cipher) { ECIES::Encryptor e; diff --git a/libdevcrypto/CryptoPP.h b/libdevcrypto/CryptoPP.h index fa9d92aa1..3464606f6 100644 --- a/libdevcrypto/CryptoPP.h +++ b/libdevcrypto/CryptoPP.h @@ -81,6 +81,15 @@ public: /// Decrypts text (replace input). void decrypt(Secret const& _k, bytes& io_text); + /// Temporary; to replace encrypt once interop w/go is passing. + void encryptECIES(Public const& _k, bytes& io_cipher); + + /// Temporary; to replace decrypt once interop w/go is passing. + void decryptECIES(Secret const& _k, bytes& io_text); + + /// Key derivation function used by ECIES. + bytes eciesKDF(Secret _z, bytes _s1, unsigned kdBitLen = 256); + /// @returns siganture of message. Signature sign(Secret const& _k, bytesConstRef _message); diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index 669d1eb0c..8d9994003 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -394,12 +394,12 @@ void PeerHandshake::transition(boost::system::error_code _ech) { clog(NetConnect) << "devp2p.connect.egress sending auth"; // egress: tx auth - asserts(remote); + asserts((bool)remote); auth.resize(Signature::size + h256::size + Public::size + h256::size + 1); - bytesConstRef sig(&auth[0], Signature::size); - bytesConstRef hepubk(&auth[Signature::size], h256::size); - bytesConstRef pubk(&auth[Signature::size + h256::size], Public::size); - bytesConstRef nonce(&auth[Signature::size + h256::size + Public::size], h256::size); + bytesRef sig(&auth[0], Signature::size); + bytesRef hepubk(&auth[Signature::size], h256::size); + bytesRef pubk(&auth[Signature::size + h256::size], Public::size); + bytesRef nonce(&auth[Signature::size + h256::size + Public::size], h256::size); // E(remote-pubk, S(ecdhe-random, ecdh-shared-secret^nonce) || H(ecdhe-random-pubk) || pubk || nonce || 0x0) crypto::ecdh::agree(host->m_alias.sec(), remote, ss); @@ -469,8 +469,8 @@ void PeerHandshake::transition(boost::system::error_code _ech) clog(NetConnect) << "devp2p.connect.ingress sending ack"; // ingress: tx ack ack.resize(Public::size + h256::size + 1); - bytesConstRef epubk(&ack[0], Public::size); - bytesConstRef nonce(&ack[Public::size], h256::size); + bytesRef epubk(&ack[0], Public::size); + bytesRef nonce(&ack[Public::size], h256::size); ecdhe.pubkey().ref().copyTo(epubk); this->nonce.ref().copyTo(nonce); ack[ack.size() - 1] = 0x0; @@ -489,7 +489,7 @@ void PeerHandshake::transition(boost::system::error_code _ech) clog(NetConnect) << "devp2p.connect.ingress sending magic sequence"; PeerSecrets* k = new PeerSecrets; bytes keyMaterialBytes(512); - bytesConstRef keyMaterial(&keyMaterialBytes); + bytesRef keyMaterial(&keyMaterialBytes); ecdhe.agree(remoteEphemeral, ess); ess.ref().copyTo(keyMaterial.cropped(0, h256::size)); @@ -521,10 +521,10 @@ void PeerHandshake::transition(boost::system::error_code _ech) // This test will be replaced with protocol-capabilities information (was Hello packet) // TESTING: send encrypt magic sequence bytes magic {0x22,0x40,0x08,0x91}; - encryptSymNoAuth(k->encryptK, &magic, k->magicCipherAndMac, h256()); + encryptSymNoAuth(k->encryptK, &magic, k->magicCipherAndMac, h128()); k->magicCipherAndMac.resize(k->magicCipherAndMac.size() + 32); sha3mac(k->egressMac.ref(), &magic, k->egressMac.ref()); - k->egressMac.ref().copyTo(bytesConstRef(&k->magicCipherAndMac).cropped(k->magicCipherAndMac.size() - 32, 32)); + k->egressMac.ref().copyTo(bytesRef(&k->magicCipherAndMac).cropped(k->magicCipherAndMac.size() - 32, 32)); clog(NetConnect) << "devp2p.connect.egress txrx magic sequence"; k->recvdMagicCipherAndMac.resize(k->magicCipherAndMac.size()); @@ -554,7 +554,7 @@ void PeerHandshake::transition(boost::system::error_code _ech) /// capabilities handshake (encrypted magic sequence is placeholder) bytes decryptedMagic; - decryptSymNoAuth(k->encryptK, h256(), &k->recvdMagicCipherAndMac, decryptedMagic); + decryptSymNoAuth(k->encryptK, h128(), &k->recvdMagicCipherAndMac, decryptedMagic); if (decryptedMagic[0] == 0x22 && decryptedMagic[1] == 0x40 && decryptedMagic[2] == 0x08 && decryptedMagic[3] == 0x91) { shared_ptr p; diff --git a/test/crypto.cpp b/test/crypto.cpp index 5dec3dd6d..56fb8a51c 100644 --- a/test/crypto.cpp +++ b/test/crypto.cpp @@ -228,6 +228,51 @@ BOOST_AUTO_TEST_CASE(cryptopp_ecdsa_sipaseckp256k1) } } +BOOST_AUTO_TEST_CASE(ecies_kdf) +{ + KeyPair local = KeyPair::create(); + KeyPair remote = KeyPair::create(); + // nonce + Secret z1; + ecdh::agree(local.sec(), remote.pub(), z1); + auto key1 = s_secp256k1.eciesKDF(z1, bytes(), 512); + bytesConstRef eKey1 = bytesConstRef(&key1).cropped(0, 32); + bytesRef mKey1 = bytesRef(&key1).cropped(32, 32); + sha3(mKey1, mKey1); + + Secret z2; + ecdh::agree(remote.sec(), local.pub(), z2); + auto key2 = s_secp256k1.eciesKDF(z2, bytes(), 512); + bytesConstRef eKey2 = bytesConstRef(&key2).cropped(0, 32); + bytesRef mKey2 = bytesRef(&key2).cropped(32, 32); + sha3(mKey2, mKey2); + + BOOST_REQUIRE(eKey1.toBytes() == eKey2.toBytes()); + BOOST_REQUIRE(mKey1.toBytes() == mKey2.toBytes()); + + BOOST_REQUIRE((u256)h256(z1) > 0); + BOOST_REQUIRE(z1 == z2); + + BOOST_REQUIRE(key1.size() > 0 && ((u512)h512(key1)) > 0); + BOOST_REQUIRE(key1 == key2); +} + +BOOST_AUTO_TEST_CASE(ecies_standard) +{ + KeyPair k = KeyPair::create(); + + string message("Now is the time for all good persons to come to the aid of humanity."); + string original = message; + bytes b = asBytes(message); + + s_secp256k1.encryptECIES(k.pub(), b); + BOOST_REQUIRE(b != asBytes(original)); + BOOST_REQUIRE(b.size() > 0 && ((u128)h128(b)) > 0); + + s_secp256k1.decryptECIES(k.sec(), b); + BOOST_REQUIRE(bytesConstRef(&b).cropped(0, original.size()).toBytes() == asBytes(original)); +} + BOOST_AUTO_TEST_CASE(ecies_eckeypair) { KeyPair k = KeyPair::create(); @@ -341,10 +386,10 @@ BOOST_AUTO_TEST_CASE(handshakeNew) bytes auth(Signature::size + h256::size + Public::size + h256::size + 1); Secret ssA; { - bytesConstRef sig(&auth[0], Signature::size); - bytesConstRef hepubk(&auth[Signature::size], h256::size); - bytesConstRef pubk(&auth[Signature::size + h256::size], Public::size); - bytesConstRef nonce(&auth[Signature::size + h256::size + Public::size], h256::size); + bytesRef sig(&auth[0], Signature::size); + bytesRef hepubk(&auth[Signature::size], h256::size); + bytesRef pubk(&auth[Signature::size + h256::size], Public::size); + bytesRef nonce(&auth[Signature::size + h256::size + Public::size], h256::size); crypto::ecdh::agree(nodeA.sec(), nodeB.pub(), ssA); sign(eA.seckey(), ssA ^ nonceA).ref().copyTo(sig); @@ -371,8 +416,8 @@ BOOST_AUTO_TEST_CASE(handshakeNew) bytesConstRef pubk(&authdecrypted[Signature::size + h256::size], Public::size); pubk.copyTo(node.ref()); - bytesConstRef epubk(&ack[0], Public::size); - bytesConstRef nonce(&ack[Public::size], h256::size); + bytesRef epubk(&ack[0], Public::size); + bytesRef nonce(&ack[Public::size], h256::size); eB.pubkey().ref().copyTo(epubk); nonceB.ref().copyTo(nonce); @@ -398,7 +443,7 @@ BOOST_AUTO_TEST_CASE(handshakeNew) bytesConstRef ackRef(&ackdecrypted); Public eBAck; h256 nonceBAck; - ackRef.cropped(0, Public::size).copyTo(bytesConstRef(eBAck.data(), Public::size)); + ackRef.cropped(0, Public::size).copyTo(bytesRef(eBAck.data(), Public::size)); ackRef.cropped(Public::size, h256::size).copyTo(nonceBAck.ref()); BOOST_REQUIRE_EQUAL(eBAck, eB.pubkey()); BOOST_REQUIRE_EQUAL(nonceBAck, nonceB); @@ -406,7 +451,7 @@ BOOST_AUTO_TEST_CASE(handshakeNew) // TODO: export ess and require equal to b bytes keyMaterialBytes(512); - bytesConstRef keyMaterial(&keyMaterialBytes); + bytesRef keyMaterial(&keyMaterialBytes); h256 ess; // todo: ecdh-agree should be able to output bytes @@ -473,7 +518,7 @@ BOOST_AUTO_TEST_CASE(handshakeNew) BOOST_REQUIRE_EQUAL(eAAuth, eA.pubkey()); bytes keyMaterialBytes(512); - bytesConstRef keyMaterial(&keyMaterialBytes); + bytesRef keyMaterial(&keyMaterialBytes); h256 ess; // todo: ecdh-agree should be able to output bytes @@ -519,24 +564,29 @@ BOOST_AUTO_TEST_CASE(ecdhe_aes128_ctr_sha3mac) } -BOOST_AUTO_TEST_CASE(crypto_rlpxwire) +BOOST_AUTO_TEST_CASE(ecies_aes128_ctr_unaligned) { Secret encryptK(sha3("...")); h256 egressMac(sha3("+++")); // TESTING: send encrypt magic sequence bytes magic {0x22,0x40,0x08,0x91}; bytes magicCipherAndMac; - encryptSymNoAuth(encryptK, &magic, magicCipherAndMac, h256()); + encryptSymNoAuth(encryptK, &magic, magicCipherAndMac, h128()); + magicCipherAndMac.resize(magicCipherAndMac.size() + 32); sha3mac(egressMac.ref(), &magic, egressMac.ref()); - egressMac.ref().copyTo(bytesConstRef(&magicCipherAndMac).cropped(magicCipherAndMac.size() - 32, 32)); + egressMac.ref().copyTo(bytesRef(&magicCipherAndMac).cropped(magicCipherAndMac.size() - 32, 32)); bytes plaintext; bytesConstRef cipher(&magicCipherAndMac[0], magicCipherAndMac.size() - 32); - decryptSymNoAuth(encryptK, h256(), cipher, plaintext); + decryptSymNoAuth(encryptK, h128(), cipher, plaintext); + + plaintext.resize(magic.size()); + BOOST_REQUIRE(plaintext.size() > 0); + BOOST_REQUIRE(magic == plaintext); } -BOOST_AUTO_TEST_CASE(crypto_aes128_ctr) +BOOST_AUTO_TEST_CASE(ecies_aes128_ctr) { Secret k(sha3("0xAAAA")); string m = "AAAAAAAAAAAAAAAA"; From bde85adee736ee02d5a951f66c71407bbe6a09db Mon Sep 17 00:00:00 2001 From: subtly Date: Mon, 2 Mar 2015 05:36:23 +0100 Subject: [PATCH 015/100] implement ecies mac tag --- libdevcore/vector_ref.h | 2 +- libdevcrypto/CryptoPP.cpp | 43 +++++++++++++++++++++++---------------- libdevcrypto/CryptoPP.h | 3 ++- 3 files changed, 28 insertions(+), 20 deletions(-) diff --git a/libdevcore/vector_ref.h b/libdevcore/vector_ref.h index 42633f6f1..cdf4f8478 100644 --- a/libdevcore/vector_ref.h +++ b/libdevcore/vector_ref.h @@ -40,7 +40,7 @@ public: bool empty() const { return !m_count; } vector_ref<_T> next() const { return vector_ref<_T>(m_data + m_count, m_count); } vector_ref<_T> cropped(size_t _begin, size_t _count = ~size_t(0)) const { if (m_data && _begin + std::max(size_t(0), _count) <= m_count) return vector_ref<_T>(m_data + _begin, _count == ~size_t(0) ? m_count - _begin : _count); else return vector_ref<_T>(); } - void retarget(_T const* _d, size_t _s) { m_data = _d; m_count = _s; } + void retarget(_T* _d, size_t _s) { m_data = _d; m_count = _s; } void retarget(std::vector<_T> const& _t) { m_data = _t.data(); m_count = _t.size(); } void copyTo(vector_ref::type> _t) const { memcpy(_t.data(), m_data, std::min(_t.size(), m_count) * sizeof(_T)); } void populate(vector_ref::type> _t) const { copyTo(_t); memset(_t.data() + m_count, 0, std::max(_t.size(), m_count) - m_count); } diff --git a/libdevcrypto/CryptoPP.cpp b/libdevcrypto/CryptoPP.cpp index 47061d6d0..e3c811a14 100644 --- a/libdevcrypto/CryptoPP.cpp +++ b/libdevcrypto/CryptoPP.cpp @@ -34,7 +34,7 @@ static_assert(dev::Signature::size == 65, "Signature must be 65 bytes."); bytes Secp256k1::eciesKDF(Secret _z, bytes _s1, unsigned kdBitLen) { - // similar to go ecies implementation + // interop w/go ecies implementation if (!_s1.size()) { @@ -76,9 +76,7 @@ bytes Secp256k1::eciesKDF(Secret _z, bytes _s1, unsigned kdBitLen) void Secp256k1::encryptECIES(Public const& _k, bytes& io_cipher) { - // similar to go ecies implementation - // todo: s1/s2/tag - + // interop w/go ecies implementation auto r = KeyPair::create(); h256 z; ecdh::agree(r.sec(), _k, z); @@ -91,38 +89,34 @@ void Secp256k1::encryptECIES(Public const& _k, bytes& io_cipher) encryptSymNoAuth(*(Secret*)eKey.data(), bytesConstRef(&io_cipher), cipherText, h128()); if (!cipherText.size()) return; - -// d := messageTag(params.Hash, Km, em, s2) -// copy(ct[len(Rb)+len(em):], d) - + bytes msg(1 + Public::size + cipherText.size() + 32); msg[0] = 0x04; r.pub().ref().copyTo(bytesRef(&msg).cropped(1, Public::size)); bytesRef msgCipherRef = bytesRef(&msg).cropped(1 + Public::size, cipherText.size()); bytesConstRef(&cipherText).copyTo(msgCipherRef); + // tag message + CryptoPP::HMAC ctx(mKey.data(), mKey.size()); + ctx.Update(cipherText.data(), cipherText.size()); + ctx.Final(msg.data() + 1 + Public::size + cipherText.size()); + io_cipher.resize(msg.size()); io_cipher.swap(msg); } -void eciesMessageTag() +bool Secp256k1::decryptECIES(Secret const& _k, bytes& io_text) { + // interop w/go ecies implementation -} - -void Secp256k1::decryptECIES(Secret const& _k, bytes& io_text) -{ - // similar to go ecies implementation - // todo: s1/s2 - // io_cipher[0] must be 2, 3, or 4, else invalidpublickey if (io_text[0] < 2 || io_text[0] > 4) // invalid message: publickey - return; + return false; if (io_text.size() < (1 + Public::size + h256::size + 1)) // invalid message: length - return; + return false; h256 z; ecdh::agree(_k, *(Public*)(io_text.data()+1), z); @@ -134,9 +128,22 @@ void Secp256k1::decryptECIES(Secret const& _k, bytes& io_text) bytes plain; size_t cipherLen = io_text.size() - 1 - Public::size - h256::size; bytesConstRef cipher(io_text.data() + 1 + Public::size, cipherLen); + bytesConstRef msgMac(cipher.data() + cipher.size(), h256::size); + + // verify tag + CryptoPP::HMAC ctx(mKey.data(), mKey.size()); + ctx.Update(cipher.data(), cipher.size()); + h256 mac; + ctx.Final(mac.data()); + for (unsigned i = 0; i < h256::size; i++) + if (mac[i] != msgMac[i]) + return false; + decryptSymNoAuth(*(Secret*)eKey.data(), h128(), cipher, plain); io_text.resize(plain.size()); io_text.swap(plain); + + return true; } void Secp256k1::encrypt(Public const& _k, bytes& io_cipher) diff --git a/libdevcrypto/CryptoPP.h b/libdevcrypto/CryptoPP.h index 3464606f6..e0f5f16ba 100644 --- a/libdevcrypto/CryptoPP.h +++ b/libdevcrypto/CryptoPP.h @@ -65,6 +65,7 @@ inline Integer secretToExponent(Secret const& _s) { return std::move(Integer(_s. /** * CryptoPP secp256k1 algorithms. + * @todo Collect ECIES methods into class. */ class Secp256k1 { @@ -85,7 +86,7 @@ public: void encryptECIES(Public const& _k, bytes& io_cipher); /// Temporary; to replace decrypt once interop w/go is passing. - void decryptECIES(Secret const& _k, bytes& io_text); + bool decryptECIES(Secret const& _k, bytes& io_text); /// Key derivation function used by ECIES. bytes eciesKDF(Secret _z, bytes _s1, unsigned kdBitLen = 256); From 30e75e265948f4a846adb4c484bd03c6c7b3bedb Mon Sep 17 00:00:00 2001 From: subtly Date: Mon, 2 Mar 2015 06:10:42 +0100 Subject: [PATCH 016/100] initial ecies interop w/go --- libdevcrypto/Common.cpp | 16 ++++++++++++++++ libdevcrypto/Common.h | 2 ++ libdevcrypto/CryptoPP.cpp | 18 ++++++++++-------- libp2p/Host.cpp | 30 ++++++++++++++++++++++-------- libp2p/Host.h | 1 + 5 files changed, 51 insertions(+), 16 deletions(-) diff --git a/libdevcrypto/Common.cpp b/libdevcrypto/Common.cpp index 7e3c403cd..e826d6157 100644 --- a/libdevcrypto/Common.cpp +++ b/libdevcrypto/Common.cpp @@ -82,6 +82,22 @@ bool dev::decrypt(Secret const& _k, bytesConstRef _cipher, bytes& o_plaintext) return true; } +void dev::encryptECIES(Public const& _k, bytesConstRef _plain, bytes& o_cipher) +{ + bytes io = _plain.toBytes(); + s_secp256k1.encryptECIES(_k, io); + o_cipher = std::move(io); +} + +bool dev::decryptECIES(Secret const& _k, bytesConstRef _cipher, bytes& o_plaintext) +{ + bytes io = _cipher.toBytes(); + if (!s_secp256k1.decryptECIES(_k, io)) + return false; + o_plaintext = std::move(io); + return true; +} + void dev::encryptSym(Secret const& _k, bytesConstRef _plain, bytes& o_cipher) { // TOOD: @alex @subtly do this properly. diff --git a/libdevcrypto/Common.h b/libdevcrypto/Common.h index 92a13f9a5..2a5658991 100644 --- a/libdevcrypto/Common.h +++ b/libdevcrypto/Common.h @@ -96,6 +96,8 @@ void encryptSym(Secret const& _k, bytesConstRef _plain, bytes& o_cipher); /// Symmetric decryption. bool decryptSym(Secret const& _k, bytesConstRef _cipher, bytes& o_plaintext); +void encryptECIES(Public const& _k, bytesConstRef _plain, bytes& o_cipher); +bool decryptECIES(Secret const& _k, bytesConstRef _cipher, bytes& o_plaintext); h128 encryptSymNoAuth(Secret const& _k, bytesConstRef _plain, bytes& o_cipher); h128 encryptSymNoAuth(Secret const& _k, bytesConstRef _plain, bytes& o_cipher, h128 const& _iv); bool decryptSymNoAuth(Secret const& _k, h128 const& _iv, bytesConstRef _cipher, bytes& o_plaintext); diff --git a/libdevcrypto/CryptoPP.cpp b/libdevcrypto/CryptoPP.cpp index e3c811a14..ba53cb7a7 100644 --- a/libdevcrypto/CryptoPP.cpp +++ b/libdevcrypto/CryptoPP.cpp @@ -90,16 +90,17 @@ void Secp256k1::encryptECIES(Public const& _k, bytes& io_cipher) if (!cipherText.size()) return; - bytes msg(1 + Public::size + cipherText.size() + 32); + bytes msg(1 + Public::size + h128::size + cipherText.size() + 32); msg[0] = 0x04; r.pub().ref().copyTo(bytesRef(&msg).cropped(1, Public::size)); - bytesRef msgCipherRef = bytesRef(&msg).cropped(1 + Public::size, cipherText.size()); + bytesRef msgCipherRef = bytesRef(&msg).cropped(1 + Public::size + h128::size, cipherText.size()); bytesConstRef(&cipherText).copyTo(msgCipherRef); // tag message CryptoPP::HMAC ctx(mKey.data(), mKey.size()); - ctx.Update(cipherText.data(), cipherText.size()); - ctx.Final(msg.data() + 1 + Public::size + cipherText.size()); + bytesConstRef cipherWithIV = bytesRef(&msg).cropped(1 + Public::size, h128::size + cipherText.size()); + ctx.Update(cipherWithIV.data(), cipherWithIV.size()); + ctx.Final(msg.data() + 1 + Public::size + cipherWithIV.size()); io_cipher.resize(msg.size()); io_cipher.swap(msg); @@ -114,7 +115,7 @@ bool Secp256k1::decryptECIES(Secret const& _k, bytes& io_text) // invalid message: publickey return false; - if (io_text.size() < (1 + Public::size + h256::size + 1)) + if (io_text.size() < (1 + Public::size + h128::size + 1 + h256::size)) // invalid message: length return false; @@ -126,13 +127,14 @@ bool Secp256k1::decryptECIES(Secret const& _k, bytes& io_text) sha3(mKey, mKey); bytes plain; - size_t cipherLen = io_text.size() - 1 - Public::size - h256::size; - bytesConstRef cipher(io_text.data() + 1 + Public::size, cipherLen); + size_t cipherLen = io_text.size() - 1 - Public::size - h128::size - h256::size; + bytesConstRef cipherWithIV(io_text.data() + 1 + Public::size, h128::size + cipherLen); + bytesConstRef cipher = cipherWithIV.cropped(h128::size, cipherLen); bytesConstRef msgMac(cipher.data() + cipher.size(), h256::size); // verify tag CryptoPP::HMAC ctx(mKey.data(), mKey.size()); - ctx.Update(cipher.data(), cipher.size()); + ctx.Update(cipherWithIV.data(), cipherWithIV.size()); h256 mac; ctx.Final(mac.data()); for (unsigned i = 0; i < h256::size; i++) diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index 8d9994003..67a701eb3 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -408,7 +408,7 @@ void PeerHandshake::transition(boost::system::error_code _ech) host->m_alias.pub().ref().copyTo(pubk); this->nonce.ref().copyTo(nonce); auth[auth.size() - 1] = 0x0; - encrypt(remote, &auth, authCipher); + encryptECIES(remote, &auth, authCipher); ba::async_write(*socket, ba::buffer(authCipher), [this, self](boost::system::error_code ec, std::size_t) { @@ -419,14 +419,21 @@ void PeerHandshake::transition(boost::system::error_code _ech) { clog(NetConnect) << "devp2p.connect.ingress recving auth"; // ingress: rx auth - authCipher.resize(279); - ba::async_read(*socket, ba::buffer(authCipher, 279), [this, self](boost::system::error_code ec, std::size_t) + authCipher.resize(321); + ba::async_read(*socket, ba::buffer(authCipher, 321), [this, self](boost::system::error_code ec, std::size_t) { if (ec) transition(ec); else { - decrypt(host->m_alias.sec(), bytesConstRef(&authCipher), auth); + if (!decryptECIES(host->m_alias.sec(), bytesConstRef(&authCipher), auth)) + { + clog(NetWarn) << "devp2p.connect.egress recving auth decrypt failed"; + nextState = Error; + transition(); + return; + } + bytesConstRef sig(&auth[0], Signature::size); bytesConstRef hepubk(&auth[Signature::size], h256::size); bytesConstRef pubk(&auth[Signature::size + h256::size], Public::size); @@ -450,14 +457,21 @@ void PeerHandshake::transition(boost::system::error_code _ech) { clog(NetConnect) << "devp2p.connect.egress recving ack"; // egress: rx ack - ackCipher.resize(182); - ba::async_read(*socket, ba::buffer(ackCipher, 182), [this, self](boost::system::error_code ec, std::size_t) + ackCipher.resize(225); + ba::async_read(*socket, ba::buffer(ackCipher, 225), [this, self](boost::system::error_code ec, std::size_t) { if (ec) transition(ec); else { - decrypt(host->m_alias.sec(), bytesConstRef(&ackCipher), ack); + if (!decryptECIES(host->m_alias.sec(), bytesConstRef(&ackCipher), ack)) + { + clog(NetWarn) << "devp2p.connect.egress recving ack decrypt failed"; + nextState = Error; + transition(); + return; + } + bytesConstRef(&ack).cropped(0, Public::size).copyTo(remoteEphemeral.ref()); bytesConstRef(&ack).cropped(Public::size, h256::size).copyTo(remoteNonce.ref()); transition(); @@ -474,7 +488,7 @@ void PeerHandshake::transition(boost::system::error_code _ech) ecdhe.pubkey().ref().copyTo(epubk); this->nonce.ref().copyTo(nonce); ack[ack.size() - 1] = 0x0; - encrypt(remote, &ack, ackCipher); + encryptECIES(remote, &ack, ackCipher); ba::async_write(*socket, ba::buffer(ackCipher), [this, self](boost::system::error_code ec, std::size_t) { transition(ec); diff --git a/libp2p/Host.h b/libp2p/Host.h index 81016333a..86f682a1a 100644 --- a/libp2p/Host.h +++ b/libp2p/Host.h @@ -252,6 +252,7 @@ struct PeerHandshake: public std::enable_shared_from_this friend class Host; enum State { + Error = -1, New, // New->AckAuth [egress: tx auth, ingress: rx auth] AckAuth, // AckAuth->Authenticating [egress: rx ack, ingress: tx ack] Authenticating, // Authenticating [tx caps, rx caps, authenticate] From 795e2fe2ae56632627c08710d48945c750ff6208 Mon Sep 17 00:00:00 2001 From: subtly Date: Mon, 2 Mar 2015 06:47:48 +0100 Subject: [PATCH 017/100] doc methods --- libdevcrypto/Common.cpp | 2 +- libdevcrypto/Common.h | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/libdevcrypto/Common.cpp b/libdevcrypto/Common.cpp index e826d6157..08eb9bd3d 100644 --- a/libdevcrypto/Common.cpp +++ b/libdevcrypto/Common.cpp @@ -15,8 +15,8 @@ along with cpp-ethereum. If not, see . */ /** @file Common.cpp - * @author Gav Wood * @author Alex Leverington + * @author Gav Wood * @date 2014 */ diff --git a/libdevcrypto/Common.h b/libdevcrypto/Common.h index 2a5658991..1bc16fa93 100644 --- a/libdevcrypto/Common.h +++ b/libdevcrypto/Common.h @@ -15,8 +15,8 @@ along with cpp-ethereum. If not, see . */ /** @file Common.h - * @author Gav Wood * @author Alex Leverington + * @author Gav Wood * @date 2014 * * Ethereum-specific data structures & algorithms. @@ -96,10 +96,19 @@ void encryptSym(Secret const& _k, bytesConstRef _plain, bytes& o_cipher); /// Symmetric decryption. bool decryptSym(Secret const& _k, bytesConstRef _cipher, bytes& o_plaintext); +/// Encrypt payload using ECIES standard with AES-CTR. TODO: move into class. void encryptECIES(Public const& _k, bytesConstRef _plain, bytes& o_cipher); + +/// Decrypt payload using ECIES standard with AES-CTR. TODO: move into class. bool decryptECIES(Secret const& _k, bytesConstRef _cipher, bytes& o_plaintext); + +/// Encrypts payload with random IV using AES-CTR. TODO: prefix IV. h128 encryptSymNoAuth(Secret const& _k, bytesConstRef _plain, bytes& o_cipher); + +/// Encrypts payload with specified IV using AES-CTR TODO: prefix IV. h128 encryptSymNoAuth(Secret const& _k, bytesConstRef _plain, bytes& o_cipher, h128 const& _iv); + +/// Decrypts payload with specified IV TODO: prefix IV. bool decryptSymNoAuth(Secret const& _k, h128 const& _iv, bytesConstRef _cipher, bytes& o_plaintext); /// Recovers Public key from signed message hash. From e3fbf63f8fb9a5fad46e4bb188916e257578803a Mon Sep 17 00:00:00 2001 From: subtly Date: Tue, 3 Mar 2015 02:23:48 +0100 Subject: [PATCH 018/100] ecies interop fix and tests. 128-bit keys. --- libdevcrypto/Common.cpp | 23 ++++-------------- libdevcrypto/CryptoPP.cpp | 51 ++++++++++++++++++++------------------- test/crypto.cpp | 48 ++++++++++++++++++++++++++++-------- 3 files changed, 69 insertions(+), 53 deletions(-) diff --git a/libdevcrypto/Common.cpp b/libdevcrypto/Common.cpp index 08eb9bd3d..4abac7eed 100644 --- a/libdevcrypto/Common.cpp +++ b/libdevcrypto/Common.cpp @@ -118,26 +118,15 @@ h128 dev::encryptSymNoAuth(Secret const& _k, bytesConstRef _plain, bytes& o_ciph h128 dev::encryptSymNoAuth(Secret const& _k, bytesConstRef _plain, bytes& o_cipher, h128 const& _iv) { - const int c_aesBlockLen = 16; - size_t extraBytes = _plain.size() % c_aesBlockLen; - size_t trimmedSize = _plain.size() - extraBytes; - size_t paddedSize = _plain.size() + ((16 - extraBytes) % 16); - o_cipher.resize(paddedSize); - - bytes underflowBytes(16); - if (o_cipher.size() != _plain.size()) - _plain.cropped(trimmedSize, extraBytes).copyTo(&underflowBytes); - - const int c_aesKeyLen = 32; + o_cipher.resize(_plain.size()); + + const int c_aesKeyLen = 16; SecByteBlock key(_k.data(), c_aesKeyLen); try { CTR_Mode::Encryption e; e.SetKeyWithIV(key, key.size(), _iv.data()); - if (trimmedSize) - e.ProcessData(o_cipher.data(), _plain.data(), trimmedSize); - if (extraBytes) - e.ProcessData(o_cipher.data() + trimmedSize, underflowBytes.data(), underflowBytes.size()); + e.ProcessData(o_cipher.data(), _plain.data(), _plain.size()); return _iv; } catch(CryptoPP::Exception& e) @@ -150,11 +139,9 @@ h128 dev::encryptSymNoAuth(Secret const& _k, bytesConstRef _plain, bytes& o_ciph bool dev::decryptSymNoAuth(Secret const& _k, h128 const& _iv, bytesConstRef _cipher, bytes& o_plaintext) { - const int c_aesBlockLen = 16; - asserts(_cipher.size() % c_aesBlockLen == 0); o_plaintext.resize(_cipher.size()); - const int c_aesKeyLen = 32; + const size_t c_aesKeyLen = 16; SecByteBlock key(_k.data(), c_aesKeyLen); try { diff --git a/libdevcrypto/CryptoPP.cpp b/libdevcrypto/CryptoPP.cpp index ba53cb7a7..82d426a76 100644 --- a/libdevcrypto/CryptoPP.cpp +++ b/libdevcrypto/CryptoPP.cpp @@ -32,22 +32,17 @@ static_assert(dev::Secret::size == 32, "Secret key must be 32 bytes."); static_assert(dev::Public::size == 64, "Public key must be 64 bytes."); static_assert(dev::Signature::size == 65, "Signature must be 65 bytes."); -bytes Secp256k1::eciesKDF(Secret _z, bytes _s1, unsigned kdBitLen) +bytes Secp256k1::eciesKDF(Secret _z, bytes _s1, unsigned kdByteLen) { // interop w/go ecies implementation - if (!_s1.size()) - { - _s1.resize(1); - asserts(_s1[0] == 0); - } - - // for sha3, hash.blocksize is 1088 bits, but this might really be digest size - auto reps = ((kdBitLen + 7) * 8) / (32 * 8); + // for sha3, blocksize is 136 bytes + // for sha256, blocksize is 64 bytes + auto reps = ((kdByteLen + 7) * 8) / (64 * 8); bytes ctr({0, 0, 0, 1}); bytes k; CryptoPP::SHA256 ctx; - while (reps--) + for (unsigned i = 0; i <= reps; i++) { ctx.Update(ctr.data(), ctr.size()); ctx.Update(_z.data(), Secret::size); @@ -70,7 +65,7 @@ bytes Secp256k1::eciesKDF(Secret _z, bytes _s1, unsigned kdBitLen) ctr[0]++; } - k.resize(kdBitLen / 8); + k.resize(kdByteLen); return move(k); } @@ -83,7 +78,9 @@ void Secp256k1::encryptECIES(Public const& _k, bytes& io_cipher) auto key = eciesKDF(z, bytes(), 512); bytesConstRef eKey = bytesConstRef(&key).cropped(0, 32); bytesRef mKey = bytesRef(&key).cropped(32, 32); - sha3(mKey, mKey); + CryptoPP::SHA256 ctx; + ctx.Update(mKey.data(), mKey.size()); + ctx.Final(mKey.data()); bytes cipherText; encryptSymNoAuth(*(Secret*)eKey.data(), bytesConstRef(&io_cipher), cipherText, h128()); @@ -97,10 +94,10 @@ void Secp256k1::encryptECIES(Public const& _k, bytes& io_cipher) bytesConstRef(&cipherText).copyTo(msgCipherRef); // tag message - CryptoPP::HMAC ctx(mKey.data(), mKey.size()); + CryptoPP::HMAC hmacctx(mKey.data(), mKey.size()); bytesConstRef cipherWithIV = bytesRef(&msg).cropped(1 + Public::size, h128::size + cipherText.size()); - ctx.Update(cipherWithIV.data(), cipherWithIV.size()); - ctx.Final(msg.data() + 1 + Public::size + cipherWithIV.size()); + hmacctx.Update(cipherWithIV.data(), cipherWithIV.size()); + hmacctx.Final(msg.data() + 1 + Public::size + cipherWithIV.size()); io_cipher.resize(msg.size()); io_cipher.swap(msg); @@ -121,27 +118,31 @@ bool Secp256k1::decryptECIES(Secret const& _k, bytes& io_text) h256 z; ecdh::agree(_k, *(Public*)(io_text.data()+1), z); - auto key = eciesKDF(z, bytes(), 512); + auto key = eciesKDF(z, bytes(), 64); bytesConstRef eKey = bytesConstRef(&key).cropped(0, 32); - bytesRef mKey = bytesRef(&key).cropped(32, 32); - sha3(mKey, mKey); + bytesRef mKey = bytesRef(&key).cropped(16, 16); + CryptoPP::SHA256 ctx; + ctx.Update(mKey.data(), mKey.size()); + ctx.Final(mKey.data()); bytes plain; size_t cipherLen = io_text.size() - 1 - Public::size - h128::size - h256::size; bytesConstRef cipherWithIV(io_text.data() + 1 + Public::size, h128::size + cipherLen); - bytesConstRef cipher = cipherWithIV.cropped(h128::size, cipherLen); - bytesConstRef msgMac(cipher.data() + cipher.size(), h256::size); + bytesConstRef cipherIV = cipherWithIV.cropped(0, h128::size); + bytesConstRef cipherNoIV = cipherWithIV.cropped(h128::size, cipherLen); + bytesConstRef msgMac(cipherNoIV.data() + cipherLen, h256::size); + h128 iv(cipherIV.toBytes()); // verify tag - CryptoPP::HMAC ctx(mKey.data(), mKey.size()); - ctx.Update(cipherWithIV.data(), cipherWithIV.size()); + CryptoPP::HMAC hmacctx(mKey.data(), mKey.size()); + hmacctx.Update(cipherWithIV.data(), cipherWithIV.size()); h256 mac; - ctx.Final(mac.data()); + hmacctx.Final(mac.data()); for (unsigned i = 0; i < h256::size; i++) if (mac[i] != msgMac[i]) - return false; + 0; - decryptSymNoAuth(*(Secret*)eKey.data(), h128(), cipher, plain); + decryptSymNoAuth(*(Secret*)eKey.data(), iv, cipherNoIV, plain); io_text.resize(plain.size()); io_text.swap(plain); diff --git a/test/crypto.cpp b/test/crypto.cpp index 56fb8a51c..5673dd530 100644 --- a/test/crypto.cpp +++ b/test/crypto.cpp @@ -228,6 +228,44 @@ BOOST_AUTO_TEST_CASE(cryptopp_ecdsa_sipaseckp256k1) } } +BOOST_AUTO_TEST_CASE(ecies_interop_test) +{ + Secret input1(fromHex("0x0de72f1223915fa8b8bf45dffef67aef8d89792d116eb61c9a1eb02c422a4663")); + bytes expect1(fromHex("0x1d0c446f9899a3426f2b89a8cb75c14b")); + bytes test1; + test1 = s_secp256k1.eciesKDF(input1, bytes(), 16); + BOOST_REQUIRE(test1 == expect1); + + KeyPair k(Secret(fromHex("0x332143e9629eedff7d142d741f896258f5a1bfab54dab2121d3ec5000093d74b"))); + Public p(fromHex("0xf0d2b97981bd0d415a843b5dfe8ab77a30300daab3658c578f2340308a2da1a07f0821367332598b6aa4e180a41e92f4ebbae3518da847f0b1c0bbfe20bcf4e1")); + Secret agreeExpected(fromHex("0xee1418607c2fcfb57fda40380e885a707f49000a5dda056d828b7d9bd1f29a08")); + Secret agreeTest; + s_secp256k1.agree(k.sec(), p, agreeTest); + BOOST_REQUIRE(agreeExpected == agreeTest); + + KeyPair kenc(Secret(fromHex("0x472413e97f1fd58d84e28a559479e6b6902d2e8a0cee672ef38a3a35d263886b"))); + Public penc(Public(fromHex("0x7a2aa2951282279dc1171549a7112b07c38c0d97c0fe2c0ae6c4588ba15be74a04efc4f7da443f6d61f68a9279bc82b73e0cc8d090048e9f87e838ae65dd8d4c"))); + BOOST_REQUIRE(penc == kenc.pub()); + + bytes cipher1(fromHex("0x046f647e1bd8a5cd1446d31513bac233e18bdc28ec0e59d46de453137a72599533f1e97c98154343420d5f16e171e5107999a7c7f1a6e26f57bcb0d2280655d08fb148d36f1d4b28642d3bb4a136f0e33e3dd2e3cffe4b45a03fb7c5b5ea5e65617250fdc89e1a315563c20504b9d3a72555")); + bytes plainTest1 = cipher1; + bytes expectedPlain1 = asBytes("a"); + BOOST_REQUIRE(s_secp256k1.decryptECIES(kenc.sec(), plainTest1)); + BOOST_REQUIRE(plainTest1 == expectedPlain1); + + bytes cipher2(fromHex("0x0443c24d6ccef3ad095140760bb143078b3880557a06392f17c5e368502d79532bc18903d59ced4bbe858e870610ab0d5f8b7963dd5c9c4cf81128d10efd7c7aa80091563c273e996578403694673581829e25a865191bdc9954db14285b56eb0043b6288172e0d003c10f42fe413222e273d1d4340c38a2d8344d7aadcbc846ee")); + bytes plainTest2 = cipher2; + bytes expectedPlain2 = asBytes("aaaaaaaaaaaaaaaa"); + BOOST_REQUIRE(s_secp256k1.decryptECIES(kenc.sec(), plainTest2)); + BOOST_REQUIRE(plainTest2 == expectedPlain2); + + bytes cipher3(fromHex("0x04c4e40c86bb5324e017e598c6d48c19362ae527af8ab21b077284a4656c8735e62d73fb3d740acefbec30ca4c024739a1fcdff69ecaf03301eebf156eb5f17cca6f9d7a7e214a1f3f6e34d1ee0ec00ce0ef7d2b242fbfec0f276e17941f9f1bfbe26de10a15a6fac3cda039904ddd1d7e06e7b96b4878f61860e47f0b84c8ceb64f6a900ff23844f4359ae49b44154980a626d3c73226c19e")); + bytes plainTest3 = cipher3; + bytes expectedPlain3 = asBytes("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); + BOOST_REQUIRE(s_secp256k1.decryptECIES(kenc.sec(), plainTest3)); + BOOST_REQUIRE(plainTest3 == expectedPlain3); +} + BOOST_AUTO_TEST_CASE(ecies_kdf) { KeyPair local = KeyPair::create(); @@ -552,16 +590,6 @@ BOOST_AUTO_TEST_CASE(handshakeNew) -} - -BOOST_AUTO_TEST_CASE(ecdhe_aes128_ctr_sha3mac) -{ - // New connections require new ECDH keypairs - // Every new connection requires a new EC keypair - // Every new trust requires a new EC keypair - // All connections should share seed for PRF (or PRNG) for nonces - - } BOOST_AUTO_TEST_CASE(ecies_aes128_ctr_unaligned) From 8e2deaebc2a1b8a197b20ed2c16b745b83ab0e52 Mon Sep 17 00:00:00 2001 From: subtly Date: Tue, 3 Mar 2015 13:01:47 +0100 Subject: [PATCH 019/100] ECIES interop with go is a go. --- libdevcrypto/CryptoPP.cpp | 7 +++--- test/crypto.cpp | 47 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/libdevcrypto/CryptoPP.cpp b/libdevcrypto/CryptoPP.cpp index 82d426a76..cf758fb42 100644 --- a/libdevcrypto/CryptoPP.cpp +++ b/libdevcrypto/CryptoPP.cpp @@ -120,9 +120,10 @@ bool Secp256k1::decryptECIES(Secret const& _k, bytes& io_text) ecdh::agree(_k, *(Public*)(io_text.data()+1), z); auto key = eciesKDF(z, bytes(), 64); bytesConstRef eKey = bytesConstRef(&key).cropped(0, 32); - bytesRef mKey = bytesRef(&key).cropped(16, 16); + bytesRef mKeyMaterial = bytesRef(&key).cropped(16, 16); + bytes mKey(32); CryptoPP::SHA256 ctx; - ctx.Update(mKey.data(), mKey.size()); + ctx.Update(mKeyMaterial.data(), mKeyMaterial.size()); ctx.Final(mKey.data()); bytes plain; @@ -140,7 +141,7 @@ bool Secp256k1::decryptECIES(Secret const& _k, bytes& io_text) hmacctx.Final(mac.data()); for (unsigned i = 0; i < h256::size; i++) if (mac[i] != msgMac[i]) - 0; + return false; decryptSymNoAuth(*(Secret*)eKey.data(), iv, cipherNoIV, plain); io_text.resize(plain.size()); diff --git a/test/crypto.cpp b/test/crypto.cpp index 5673dd530..4a9a9dc80 100644 --- a/test/crypto.cpp +++ b/test/crypto.cpp @@ -230,12 +230,52 @@ BOOST_AUTO_TEST_CASE(cryptopp_ecdsa_sipaseckp256k1) BOOST_AUTO_TEST_CASE(ecies_interop_test) { + CryptoPP::SHA256 sha256ctx; + bytes emptyExpected(fromHex("0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")); + bytes empty; + sha256ctx.Update(empty.data(), 0); + bytes emptyTestOut(32); + sha256ctx.Final(emptyTestOut.data()); + BOOST_REQUIRE(emptyExpected == emptyTestOut); + + bytes hash1Expected(fromHex("0x8949b278bbafb8da1aaa18cb724175c5952280f74be5d29ab4b37d1b45c84b08")); + bytes hash1input(fromHex("0x55a53b55afb12affff3c")); + sha256ctx.Update(hash1input.data(), hash1input.size()); + bytes hash1Out(32); + sha256ctx.Final(hash1Out.data()); + BOOST_REQUIRE(hash1Out == hash1Expected); + + h128 hmack(fromHex("0x07a4b6dfa06369a570f2dcba2f11a18f")); + CryptoPP::HMAC hmacctx(hmack.data(), h128::size); + bytes input(fromHex("0x4dcb92ed4fc67fe86832")); + hmacctx.Update(input.data(), input.size()); + bytes hmacExpected(fromHex("0xc90b62b1a673b47df8e395e671a68bfa68070d6e2ef039598bb829398b89b9a9")); + bytes hmacOut(hmacExpected.size()); + hmacctx.Final(hmacOut.data()); + BOOST_REQUIRE(hmacExpected == hmacOut); + + // go messageTag + bytes tagSecret(fromHex("0xaf6623e52208c596e17c72cea6f1cb09")); + bytes tagInput(fromHex("0x3461282bcedace970df2")); + bytes tagExpected(fromHex("0xb3ce623bce08d5793677ba9441b22bb34d3e8a7de964206d26589df3e8eb5183")); + CryptoPP::HMAC hmactagctx(tagSecret.data(), tagSecret.size()); + hmactagctx.Update(tagInput.data(), tagInput.size()); + h256 mac; + hmactagctx.Final(mac.data()); + BOOST_REQUIRE(mac.asBytes() == tagExpected); + Secret input1(fromHex("0x0de72f1223915fa8b8bf45dffef67aef8d89792d116eb61c9a1eb02c422a4663")); bytes expect1(fromHex("0x1d0c446f9899a3426f2b89a8cb75c14b")); bytes test1; test1 = s_secp256k1.eciesKDF(input1, bytes(), 16); BOOST_REQUIRE(test1 == expect1); + Secret kdfInput2(fromHex("0x961c065873443014e0371f1ed656c586c6730bf927415757f389d92acf8268df")); + bytes kdfExpect2(fromHex("0x4050c52e6d9c08755e5a818ac66fabe478b825b1836fd5efc4d44e40d04dabcc")); + bytes kdfTest2; + kdfTest2 = s_secp256k1.eciesKDF(kdfInput2, bytes(), 32); + BOOST_REQUIRE(kdfTest2 == kdfExpect2); + KeyPair k(Secret(fromHex("0x332143e9629eedff7d142d741f896258f5a1bfab54dab2121d3ec5000093d74b"))); Public p(fromHex("0xf0d2b97981bd0d415a843b5dfe8ab77a30300daab3658c578f2340308a2da1a07f0821367332598b6aa4e180a41e92f4ebbae3518da847f0b1c0bbfe20bcf4e1")); Secret agreeExpected(fromHex("0xee1418607c2fcfb57fda40380e885a707f49000a5dda056d828b7d9bd1f29a08")); @@ -243,6 +283,13 @@ BOOST_AUTO_TEST_CASE(ecies_interop_test) s_secp256k1.agree(k.sec(), p, agreeTest); BOOST_REQUIRE(agreeExpected == agreeTest); + KeyPair kmK(Secret(fromHex("0x57baf2c62005ddec64c357d96183ebc90bf9100583280e848aa31d683cad73cb"))); + bytes kmCipher(fromHex("0x04ff2c874d0a47917c84eea0b2a4141ca95233720b5c70f81a8415bae1dc7b746b61df7558811c1d6054333907333ef9bb0cc2fbf8b34abb9730d14e0140f4553f4b15d705120af46cf653a1dc5b95b312cf8444714f95a4f7a0425b67fc064d18f4d0a528761565ca02d97faffdac23de10")); + bytes kmPlain = kmCipher; + bytes kmExpected(asBytes("a")); + BOOST_REQUIRE(s_secp256k1.decryptECIES(kmK.sec(), kmPlain)); + BOOST_REQUIRE(kmExpected == kmPlain); + KeyPair kenc(Secret(fromHex("0x472413e97f1fd58d84e28a559479e6b6902d2e8a0cee672ef38a3a35d263886b"))); Public penc(Public(fromHex("0x7a2aa2951282279dc1171549a7112b07c38c0d97c0fe2c0ae6c4588ba15be74a04efc4f7da443f6d61f68a9279bc82b73e0cc8d090048e9f87e838ae65dd8d4c"))); BOOST_REQUIRE(penc == kenc.pub()); From b0aa7ce8ef45daeb3b931a7536063a551f382a6a Mon Sep 17 00:00:00 2001 From: subtly Date: Tue, 3 Mar 2015 14:01:21 +0100 Subject: [PATCH 020/100] update encrypt to use same keying as decrypt --- libdevcrypto/CryptoPP.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/libdevcrypto/CryptoPP.cpp b/libdevcrypto/CryptoPP.cpp index cf758fb42..356317935 100644 --- a/libdevcrypto/CryptoPP.cpp +++ b/libdevcrypto/CryptoPP.cpp @@ -75,11 +75,12 @@ void Secp256k1::encryptECIES(Public const& _k, bytes& io_cipher) auto r = KeyPair::create(); h256 z; ecdh::agree(r.sec(), _k, z); - auto key = eciesKDF(z, bytes(), 512); - bytesConstRef eKey = bytesConstRef(&key).cropped(0, 32); - bytesRef mKey = bytesRef(&key).cropped(32, 32); + auto key = eciesKDF(z, bytes(), 32); + bytesConstRef eKey = bytesConstRef(&key).cropped(0, 16); + bytesRef mKeyMaterial = bytesRef(&key).cropped(16, 16); CryptoPP::SHA256 ctx; - ctx.Update(mKey.data(), mKey.size()); + ctx.Update(mKeyMaterial.data(), mKeyMaterial.size()); + bytes mKey(32); ctx.Final(mKey.data()); bytes cipherText; @@ -119,7 +120,7 @@ bool Secp256k1::decryptECIES(Secret const& _k, bytes& io_text) h256 z; ecdh::agree(_k, *(Public*)(io_text.data()+1), z); auto key = eciesKDF(z, bytes(), 64); - bytesConstRef eKey = bytesConstRef(&key).cropped(0, 32); + bytesConstRef eKey = bytesConstRef(&key).cropped(0, 16); bytesRef mKeyMaterial = bytesRef(&key).cropped(16, 16); bytes mKey(32); CryptoPP::SHA256 ctx; From e79d427902cda380e233a2ee0ecbe76fad6413ba Mon Sep 17 00:00:00 2001 From: subtly Date: Tue, 3 Mar 2015 20:56:05 +0100 Subject: [PATCH 021/100] update host for ecies interop. test sha3 output of interim digest. --- libp2p/Host.cpp | 16 ++++++++++++---- test/crypto.cpp | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 4 deletions(-) diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index 67a701eb3..b0bf097fb 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -419,8 +419,8 @@ void PeerHandshake::transition(boost::system::error_code _ech) { clog(NetConnect) << "devp2p.connect.ingress recving auth"; // ingress: rx auth - authCipher.resize(321); - ba::async_read(*socket, ba::buffer(authCipher, 321), [this, self](boost::system::error_code ec, std::size_t) + authCipher.resize(307); + ba::async_read(*socket, ba::buffer(authCipher, 307), [this, self](boost::system::error_code ec, std::size_t) { if (ec) transition(ec); @@ -457,8 +457,8 @@ void PeerHandshake::transition(boost::system::error_code _ech) { clog(NetConnect) << "devp2p.connect.egress recving ack"; // egress: rx ack - ackCipher.resize(225); - ba::async_read(*socket, ba::buffer(ackCipher, 225), [this, self](boost::system::error_code ec, std::size_t) + ackCipher.resize(210); + ba::async_read(*socket, ba::buffer(ackCipher, 210), [this, self](boost::system::error_code ec, std::size_t) { if (ec) transition(ec); @@ -532,14 +532,22 @@ void PeerHandshake::transition(boost::system::error_code _ech) bytesConstRef(&ingressCipher).copyTo(keyMaterial.cropped(h256::size, ingressCipher.size())); k->ingressMac = sha3(keyMaterial); + + + // This test will be replaced with protocol-capabilities information (was Hello packet) // TESTING: send encrypt magic sequence bytes magic {0x22,0x40,0x08,0x91}; + + + // rlpx encrypt encryptSymNoAuth(k->encryptK, &magic, k->magicCipherAndMac, h128()); k->magicCipherAndMac.resize(k->magicCipherAndMac.size() + 32); sha3mac(k->egressMac.ref(), &magic, k->egressMac.ref()); k->egressMac.ref().copyTo(bytesRef(&k->magicCipherAndMac).cropped(k->magicCipherAndMac.size() - 32, 32)); + + clog(NetConnect) << "devp2p.connect.egress txrx magic sequence"; k->recvdMagicCipherAndMac.resize(k->magicCipherAndMac.size()); diff --git a/test/crypto.cpp b/test/crypto.cpp index 4a9a9dc80..ebe8db6c5 100644 --- a/test/crypto.cpp +++ b/test/crypto.cpp @@ -228,6 +228,42 @@ BOOST_AUTO_TEST_CASE(cryptopp_ecdsa_sipaseckp256k1) } } +BOOST_AUTO_TEST_CASE(rlpx_sha3_norestart) +{ + CryptoPP::SHA3_256 ctx; + bytes input(asBytes("test")); + ctx.Update(input.data(), 4); + CryptoPP::SHA3_256 ctxCopy(ctx); + bytes interimDigest(32); + ctx.Final(interimDigest.data()); + ctx.Update(input.data(), 4); + bytes firstDigest(32); + ctx.Final(firstDigest.data()); + BOOST_REQUIRE(interimDigest == firstDigest); + + ctxCopy.Update(input.data(), 4); + bytes finalDigest(32); + ctxCopy.Final(interimDigest.data()); + BOOST_REQUIRE(interimDigest != finalDigest); + + // we can do this another way -- copy the context for final + ctxCopy.Update(input.data(), 4); + ctxCopy.Update(input.data(), 4); + CryptoPP::SHA3_256 finalCtx(ctxCopy); + bytes finalDigest2(32); + finalCtx.Final(finalDigest2.data()); + BOOST_REQUIRE(finalDigest2 == interimDigest); + ctxCopy.Update(input.data(), 4); + bytes finalDigest3(32); + finalCtx.Final(finalDigest3.data()); + BOOST_REQUIRE(finalDigest2 != finalDigest3); +} + +BOOST_AUTO_TEST_CASE(rlpx_updatemac_aesecb_sha3) +{ + +} + BOOST_AUTO_TEST_CASE(ecies_interop_test) { CryptoPP::SHA256 sha256ctx; From 69e48c2707733418060826c1399eaf17ae46e6ae Mon Sep 17 00:00:00 2001 From: subtly Date: Tue, 3 Mar 2015 21:24:31 +0100 Subject: [PATCH 022/100] initial rlpx framing classes --- libp2p/Host.cpp | 3 + libp2p/RLPxHandshake.cpp | 390 +++++++++++++++++++++++++++++++++++++++ libp2p/RLPxHandshake.h | 146 +++++++++++++++ 3 files changed, 539 insertions(+) create mode 100644 libp2p/RLPxHandshake.cpp create mode 100644 libp2p/RLPxHandshake.h diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index b0bf097fb..0062722c6 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -39,6 +39,8 @@ using namespace std; using namespace dev; using namespace dev::p2p; +#include "RLPxHandshake.h" + HostNodeTableHandler::HostNodeTableHandler(Host& _host): m_host(_host) {} void HostNodeTableHandler::processEvent(NodeId const& _n, NodeTableEventType const& _e) @@ -501,6 +503,7 @@ void PeerHandshake::transition(boost::system::error_code _ech) clog(NetConnect) << "devp2p.connect.egress sending magic sequence"; else clog(NetConnect) << "devp2p.connect.ingress sending magic sequence"; + PeerSecrets* k = new PeerSecrets; bytes keyMaterialBytes(512); bytesRef keyMaterial(&keyMaterialBytes); diff --git a/libp2p/RLPxHandshake.cpp b/libp2p/RLPxHandshake.cpp new file mode 100644 index 000000000..37b75f1c9 --- /dev/null +++ b/libp2p/RLPxHandshake.cpp @@ -0,0 +1,390 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . + */ +/** @file RLPXHandshake.cpp + * @author Alex Leverington + * @date 2015 + */ + +#include "Host.h" +#include "Session.h" +#include "Peer.h" +#include "PeerHandshake.h" +using namespace std; +using namespace dev; +using namespace dev::p2p; +using namespace CryptoPP; + +RLPXFrameIO::RLPXFrameIO(bool _originated, Secret const& _ephemeralShared, bytesConstRef _authCipher, bytesConstRef _ackCipher): m_keys(h128(), h128()), m_macUpdateEncryptor(sha3("test").data(), 16) +{ + // we need: + // originated? + // Secret == output of ecdhe agreement + // authCipher + // ackCipher + + bytes keyMaterialBytes(512); + bytesRef keyMaterial(&keyMaterialBytes); + +// ecdhe.agree(remoteEphemeral, ess); + _ephemeralShared.ref().copyTo(keyMaterial.cropped(0, h256::size)); +// ss.ref().copyTo(keyMaterial.cropped(h256::size, h256::size)); +// // auto token = sha3(ssA); +// k->encryptK = sha3(keyMaterial); +// k->encryptK.ref().copyTo(keyMaterial.cropped(h256::size, h256::size)); +// k->macK = sha3(keyMaterial); +// +// // Initiator egress-mac: sha3(mac-secret^recipient-nonce || auth-sent-init) +// // ingress-mac: sha3(mac-secret^initiator-nonce || auth-recvd-ack) +// // Recipient egress-mac: sha3(mac-secret^initiator-nonce || auth-sent-ack) +// // ingress-mac: sha3(mac-secret^recipient-nonce || auth-recvd-init) +// +// bytes const& egressCipher = _originated ? authCipher : ackCipher; +// keyMaterialBytes.resize(h256::size + egressCipher.size()); +// keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); +// (k->macK ^ remoteNonce).ref().copyTo(keyMaterial); +// bytesConstRef(&egressCipher).copyTo(keyMaterial.cropped(h256::size, egressCipher.size())); +// k->egressMac = sha3(keyMaterial); +// +// bytes const& ingressCipher = _originated ? ackCipher : authCipher; +// keyMaterialBytes.resize(h256::size + ingressCipher.size()); +// keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); +// (k->macK ^ nonce).ref().copyTo(keyMaterial); +// bytesConstRef(&ingressCipher).copyTo(keyMaterial.cropped(h256::size, ingressCipher.size())); +// k->ingressMac = sha3(keyMaterial); + +} + +void RLPXFrameIO::writeFullPacketFrame(bytesConstRef _packet) +{ + +} + +void RLPXFrameIO::writeHeader(bi::tcp::socket* _socket, h128 const& _header) +{ + +} + +void RLPXFrameIO::write(bi::tcp::socket* _socket, bytesConstRef _in, bool _eof) +{ + +} + +bool RLPXFrameIO::read(bytesConstRef _in, bytes& o_out) +{ + +} + +h128 RLPXFrameIO::egressDigest() +{ + SHA3_256 h(m_egressMac); + h128 digest; + h.TruncatedFinal(digest.data(), h128::size); + return move(digest); +} + +h128 RLPXFrameIO::ingressDigest() +{ + SHA3_256 h(m_ingressMac); + h128 digest; + h.TruncatedFinal(digest.data(), h128::size); + return move(digest); +} + +void RLPXFrameIO::updateEgressMACWithHeader(h128 const& _headerCipher) +{ + m_egressMac.Update(_headerCipher.data(), h128::size); + updateMAC(m_egressMac); +} + +void RLPXFrameIO::updateEgressMACWithEndOfFrame(bytesConstRef _cipher) +{ + m_egressMac.Update(_cipher.data(), _cipher.size()); + updateMAC(m_egressMac); +} + +void RLPXFrameIO::updateIngressMACWithHeader(bytesConstRef _headerCipher) +{ + m_ingressMac.Update(_headerCipher.data(), h128::size); + updateMAC(m_ingressMac); +} + +void RLPXFrameIO::updateIngressMACWithEndOfFrame(bytesConstRef _cipher) +{ + m_ingressMac.Update(_cipher.data(), _cipher.size()); + updateMAC(m_ingressMac); +} + +void RLPXFrameIO::updateMAC(SHA3_256& _mac) +{ + SHA3_256 prevDigest(_mac); + h128 prevDigestOut; + prevDigest.TruncatedFinal(prevDigestOut.data(), h128::size); + + h128 encDigest; + m_macUpdateEncryptor.ProcessData(encDigest.data(), prevDigestOut.data(), h128::size); + encDigest ^= prevDigestOut; + + // update mac for final digest + _mac.Update(encDigest.data(), h256::size); +} + + +void RLPXHandshake::generateAuth() +{ + auth.resize(Signature::size + h256::size + Public::size + h256::size + 1); + bytesRef sig(&auth[0], Signature::size); + bytesRef hepubk(&auth[Signature::size], h256::size); + bytesRef pubk(&auth[Signature::size + h256::size], Public::size); + bytesRef nonce(&auth[Signature::size + h256::size + Public::size], h256::size); + + // E(remote-pubk, S(ecdhe-random, ecdh-shared-secret^nonce) || H(ecdhe-random-pubk) || pubk || nonce || 0x0) + crypto::ecdh::agree(host->m_alias.sec(), remote, ss); + sign(ecdhe.seckey(), ss ^ this->nonce).ref().copyTo(sig); + sha3(ecdhe.pubkey().ref(), hepubk); + host->m_alias.pub().ref().copyTo(pubk); + this->nonce.ref().copyTo(nonce); + auth[auth.size() - 1] = 0x0; + encryptECIES(remote, &auth, authCipher); +} + +void RLPXHandshake::generateAck() +{ + +} + +bool RLPXHandshake::decodeAuth() +{ + if (!decryptECIES(host->m_alias.sec(), bytesConstRef(&authCipher), auth)) + return false; + + bytesConstRef sig(&auth[0], Signature::size); + bytesConstRef hepubk(&auth[Signature::size], h256::size); + bytesConstRef pubk(&auth[Signature::size + h256::size], Public::size); + bytesConstRef nonce(&auth[Signature::size + h256::size + Public::size], h256::size); + pubk.copyTo(remote.ref()); + nonce.copyTo(remoteNonce.ref()); + + crypto::ecdh::agree(host->m_alias.sec(), remote, ss); + remoteEphemeral = recover(*(Signature*)sig.data(), ss ^ remoteNonce); + assert(sha3(remoteEphemeral) == *(h256*)hepubk.data()); + return true; +} + +bool RLPXHandshake::decodeAck() +{ + +} + +/// used for protocol handshake +bytes RLPXHandshake::frame(bytesConstRef _packet) +{ + +} + +void RLPXHandshake::transition(boost::system::error_code _ech) +{ + if (_ech || nextState == Error) + { + clog(NetConnect) << "Disconnecting " << socket->remote_endpoint() << " (Handshake Failed)"; + boost::system::error_code ec; + socket->shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); + if (socket->is_open()) + socket->close(); + return; + } + + auto self(shared_from_this()); + if (nextState == New) + { + nextState = AckAuth; + + clog(NetConnect) << "Authenticating connection for " << socket->remote_endpoint(); + + if (originated) + { + clog(NetConnect) << "p2p.connect.egress sending auth"; + generateAuth(); + ba::async_write(*socket, ba::buffer(authCipher), [this, self](boost::system::error_code ec, std::size_t) + { + transition(ec); + }); + } + else + { + clog(NetConnect) << "p2p.connect.ingress recving auth"; + authCipher.resize(321); + ba::async_read(*socket, ba::buffer(authCipher, 321), [this, self](boost::system::error_code ec, std::size_t) + { + if (ec) + transition(ec); + else if (decodeAuth()) + transition(); + else + { + clog(NetWarn) << "p2p.connect.egress recving auth decrypt failed"; + nextState = Error; + transition(); + return; + } + }); + } + } + else if (nextState == AckAuth) + { + nextState = Authenticating; + + if (originated) + { + clog(NetConnect) << "p2p.connect.egress recving ack"; + // egress: rx ack + ackCipher.resize(225); + ba::async_read(*socket, ba::buffer(ackCipher, 225), [this, self](boost::system::error_code ec, std::size_t) + { + if (ec) + transition(ec); + else + { + if (!decryptECIES(host->m_alias.sec(), bytesConstRef(&ackCipher), ack)) + { + clog(NetWarn) << "p2p.connect.egress recving ack decrypt failed"; + nextState = Error; + transition(); + return; + } + + bytesConstRef(&ack).cropped(0, Public::size).copyTo(remoteEphemeral.ref()); + bytesConstRef(&ack).cropped(Public::size, h256::size).copyTo(remoteNonce.ref()); + transition(); + } + }); + } + else + { + clog(NetConnect) << "p2p.connect.ingress sending ack"; + // ingress: tx ack + ack.resize(Public::size + h256::size + 1); + bytesRef epubk(&ack[0], Public::size); + bytesRef nonce(&ack[Public::size], h256::size); + ecdhe.pubkey().ref().copyTo(epubk); + this->nonce.ref().copyTo(nonce); + ack[ack.size() - 1] = 0x0; + encryptECIES(remote, &ack, ackCipher); + ba::async_write(*socket, ba::buffer(ackCipher), [this, self](boost::system::error_code ec, std::size_t) + { + transition(ec); + }); + } + } + else if (nextState == Authenticating) + { + if (originated) + clog(NetConnect) << "p2p.connect.egress sending magic sequence"; + else + clog(NetConnect) << "p2p.connect.ingress sending magic sequence"; + PeerSecrets* k = new PeerSecrets; + bytes keyMaterialBytes(512); + bytesRef keyMaterial(&keyMaterialBytes); + + ecdhe.agree(remoteEphemeral, ess); + ess.ref().copyTo(keyMaterial.cropped(0, h256::size)); + ss.ref().copyTo(keyMaterial.cropped(h256::size, h256::size)); + // auto token = sha3(ssA); + k->encryptK = sha3(keyMaterial); + k->encryptK.ref().copyTo(keyMaterial.cropped(h256::size, h256::size)); + k->macK = sha3(keyMaterial); + + // Initiator egress-mac: sha3(mac-secret^recipient-nonce || auth-sent-init) + // ingress-mac: sha3(mac-secret^initiator-nonce || auth-recvd-ack) + // Recipient egress-mac: sha3(mac-secret^initiator-nonce || auth-sent-ack) + // ingress-mac: sha3(mac-secret^recipient-nonce || auth-recvd-init) + + bytes const& egressCipher = originated ? authCipher : ackCipher; + keyMaterialBytes.resize(h256::size + egressCipher.size()); + keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); + (k->macK ^ remoteNonce).ref().copyTo(keyMaterial); + bytesConstRef(&egressCipher).copyTo(keyMaterial.cropped(h256::size, egressCipher.size())); + k->egressMac = sha3(keyMaterial); + + bytes const& ingressCipher = originated ? ackCipher : authCipher; + keyMaterialBytes.resize(h256::size + ingressCipher.size()); + keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); + (k->macK ^ nonce).ref().copyTo(keyMaterial); + bytesConstRef(&ingressCipher).copyTo(keyMaterial.cropped(h256::size, ingressCipher.size())); + k->ingressMac = sha3(keyMaterial); + + // This test will be replaced with protocol-capabilities information (was Hello packet) + // TESTING: send encrypt magic sequence + bytes magic {0x22,0x40,0x08,0x91}; + // rlpx encrypt + encryptSymNoAuth(k->encryptK, &magic, k->magicCipherAndMac, h128()); + k->magicCipherAndMac.resize(k->magicCipherAndMac.size() + 32); + sha3mac(k->egressMac.ref(), &magic, k->egressMac.ref()); + k->egressMac.ref().copyTo(bytesRef(&k->magicCipherAndMac).cropped(k->magicCipherAndMac.size() - 32, 32)); + + clog(NetConnect) << "p2p.connect.egress txrx magic sequence"; + k->recvdMagicCipherAndMac.resize(k->magicCipherAndMac.size()); + + ba::async_write(*socket, ba::buffer(k->magicCipherAndMac), [this, self, k, magic](boost::system::error_code ec, std::size_t) + { + if (ec) + { + delete k; + transition(ec); + return; + } + + ba::async_read(*socket, ba::buffer(k->recvdMagicCipherAndMac, k->magicCipherAndMac.size()), [this, self, k, magic](boost::system::error_code ec, std::size_t) + { + if (originated) + clog(NetNote) << "p2p.connect.egress recving magic sequence"; + else + clog(NetNote) << "p2p.connect.ingress recving magic sequence"; + + if (ec) + { + delete k; + transition(ec); + return; + } + + /// capabilities handshake (encrypted magic sequence is placeholder) + bytes decryptedMagic; + decryptSymNoAuth(k->encryptK, h128(), &k->recvdMagicCipherAndMac, decryptedMagic); + if (decryptedMagic[0] == 0x22 && decryptedMagic[1] == 0x40 && decryptedMagic[2] == 0x08 && decryptedMagic[3] == 0x91) + { + shared_ptr p; + p = host->m_peers[remote]; + if (!p) + { + p.reset(new Peer()); + p->id = remote; + } + p->endpoint.tcp.address(socket->remote_endpoint().address()); + p->m_lastDisconnect = NoDisconnect; + p->m_lastConnected = std::chrono::system_clock::now(); + p->m_failedAttempts = 0; + + auto ps = std::make_shared(host, move(*socket), p); + ps->start(); + } + + // todo: PeerSession will take ownership of k and use it to encrypt wireline. + delete k; + }); + }); + } +} diff --git a/libp2p/RLPxHandshake.h b/libp2p/RLPxHandshake.h new file mode 100644 index 000000000..e0be14f55 --- /dev/null +++ b/libp2p/RLPxHandshake.h @@ -0,0 +1,146 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . + */ +/** @file RLPXHandshake.h + * @author Alex Leverington + * @date 2015 + */ + + +#pragma once + +#include +#include +#include +#include +#include "Common.h" +namespace ba = boost::asio; +namespace bi = boost::asio::ip; + +namespace dev +{ +namespace p2p +{ + +class RLPXFrameIO +{ +public: + RLPXFrameIO(bool _originated, Secret const& _ephemeralShared, bytesConstRef _authCipher, bytesConstRef _ackCipher); + + void writeFullPacketFrame(bytesConstRef _packet); + + void writeHeader(bi::tcp::socket* _socket, h128 const& _header); + + void write(bi::tcp::socket* _socket, bytesConstRef _in, bool _eof = false); + + bool read(bytesConstRef _in, bytes& o_out); + + h128 egressDigest(); + + h128 ingressDigest(); + + void updateEgressMACWithHeader(h128 const& _headerCipher); + + void updateEgressMACWithEndOfFrame(bytesConstRef _cipher); + + void updateIngressMACWithHeader(bytesConstRef _headerCipher); + + void updateIngressMACWithEndOfFrame(bytesConstRef _cipher); + +private: + struct RLPXSecrets + { + // ideally this will be passed ecdhe-shared-secret, auth cipher, ack cpiher + RLPXSecrets(h128 const& encK, h128 const& macK): frameEnc(encK.data(), h128::size), macEnc(macK.data(), h128::size) {} + + CryptoPP::CTR_Mode::Encryption frameEnc; + CryptoPP::ECB_Mode::Encryption macEnc; + + CryptoPP::SHA3_256 egressMac; + CryptoPP::SHA3_256 ingressMac; + }; + + void updateMAC(CryptoPP::SHA3_256& _mac); + + CryptoPP::ECB_Mode::Encryption m_macUpdateEncryptor; + CryptoPP::SHA3_256 m_egressMac; + CryptoPP::SHA3_256 m_ingressMac; + + RLPXSecrets m_keys; +}; + +struct RLPXHandshake: public std::enable_shared_from_this +{ + friend class Host; + enum State + { + Error = -1, + New, // New->AckAuth [egress: tx auth, ingress: rx auth] + AckAuth, // AckAuth->Authenticating [egress: rx ack, ingress: tx ack] + Authenticating, // Authenticating [tx caps, rx caps, authenticate] + }; + + /// Handshake for ingress connection. Takes ownership of socket. + RLPXHandshake(Host* _host, bi::tcp::socket* _socket): host(_host), socket(std::move(_socket)), originated(false) { crypto::Nonce::get().ref().copyTo(nonce.ref()); } + + /// Handshake for egress connection to _remote. Takes ownership of socket. + RLPXHandshake(Host* _host, bi::tcp::socket* _socket, NodeId _remote): host(_host), remote(_remote), socket(std::move(_socket)), originated(true) { crypto::Nonce::get().ref().copyTo(nonce.ref()); } + + ~RLPXHandshake() { delete socket; } + +protected: + void start() { transition(); } + + void generateAuth(); + bool decodeAuth(); + + void generateAck(); + bool decodeAck(); + + bytes frame(bytesConstRef _packet); + +private: + void transition(boost::system::error_code _ech = boost::system::error_code()); + + /// Current state of handshake. + State nextState = New; + + Host* host; + + /// Node id of remote host for socket. + NodeId remote; + + bi::tcp::socket* socket; + bool originated = false; + + /// Buffers for encoded and decoded handshake phases + bytes auth; + bytes authCipher; + bytes ack; + bytes ackCipher; + + Secret ss; + Secret ess; + + crypto::ECDHE ecdhe; + h256 nonce; + + Public remoteEphemeral; + h256 remoteNonce; +}; + +} +} \ No newline at end of file From 924ac4ec92ba6685f0d0022b071cc95fc98265aa Mon Sep 17 00:00:00 2001 From: subtly Date: Tue, 3 Mar 2015 23:24:42 +0100 Subject: [PATCH 023/100] pair-coding update --- libp2p/RLPxHandshake.cpp | 10 +++++----- libp2p/RLPxHandshake.h | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/libp2p/RLPxHandshake.cpp b/libp2p/RLPxHandshake.cpp index 37b75f1c9..de4e9b90f 100644 --- a/libp2p/RLPxHandshake.cpp +++ b/libp2p/RLPxHandshake.cpp @@ -22,7 +22,7 @@ #include "Host.h" #include "Session.h" #include "Peer.h" -#include "PeerHandshake.h" +#include "RLPxHandshake.h" using namespace std; using namespace dev; using namespace dev::p2p; @@ -107,7 +107,7 @@ h128 RLPXFrameIO::ingressDigest() void RLPXFrameIO::updateEgressMACWithHeader(h128 const& _headerCipher) { m_egressMac.Update(_headerCipher.data(), h128::size); - updateMAC(m_egressMac); + updateMAC(m_egressMac, *(h128*)_headerCipher.data()); } void RLPXFrameIO::updateEgressMACWithEndOfFrame(bytesConstRef _cipher) @@ -119,7 +119,7 @@ void RLPXFrameIO::updateEgressMACWithEndOfFrame(bytesConstRef _cipher) void RLPXFrameIO::updateIngressMACWithHeader(bytesConstRef _headerCipher) { m_ingressMac.Update(_headerCipher.data(), h128::size); - updateMAC(m_ingressMac); + updateMAC(m_ingressMac, *(h128*)_headerCipher.data()); } void RLPXFrameIO::updateIngressMACWithEndOfFrame(bytesConstRef _cipher) @@ -128,7 +128,7 @@ void RLPXFrameIO::updateIngressMACWithEndOfFrame(bytesConstRef _cipher) updateMAC(m_ingressMac); } -void RLPXFrameIO::updateMAC(SHA3_256& _mac) +void RLPXFrameIO::updateMAC(SHA3_256& _mac, h128 const& _seed) { SHA3_256 prevDigest(_mac); h128 prevDigestOut; @@ -136,7 +136,7 @@ void RLPXFrameIO::updateMAC(SHA3_256& _mac) h128 encDigest; m_macUpdateEncryptor.ProcessData(encDigest.data(), prevDigestOut.data(), h128::size); - encDigest ^= prevDigestOut; + encDigest ^= (!!_seed ? _seed : prevDigestOut); // update mac for final digest _mac.Update(encDigest.data(), h256::size); diff --git a/libp2p/RLPxHandshake.h b/libp2p/RLPxHandshake.h index e0be14f55..0e39dc810 100644 --- a/libp2p/RLPxHandshake.h +++ b/libp2p/RLPxHandshake.h @@ -73,7 +73,7 @@ private: CryptoPP::SHA3_256 ingressMac; }; - void updateMAC(CryptoPP::SHA3_256& _mac); + void updateMAC(CryptoPP::SHA3_256& _mac, h128 const& _seed = h128()); CryptoPP::ECB_Mode::Encryption m_macUpdateEncryptor; CryptoPP::SHA3_256 m_egressMac; From dc1bb1d28b3e695b81f4c018ac232af474d43030 Mon Sep 17 00:00:00 2001 From: subtly Date: Tue, 3 Mar 2015 23:30:25 +0100 Subject: [PATCH 024/100] build fix --- libp2p/Host.h | 9 +++++++-- libp2p/Peer.h | 2 ++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/libp2p/Host.h b/libp2p/Host.h index 86f682a1a..c34ec9f21 100644 --- a/libp2p/Host.h +++ b/libp2p/Host.h @@ -48,7 +48,7 @@ namespace dev namespace p2p { - + class Host; class HostNodeTableHandler: public NodeTableEventHandler @@ -63,7 +63,7 @@ private: Host& m_host; }; - + /** * @brief The Host class * Capabilities should be registered prior to startNetwork, since m_capabilities is not thread-safe. @@ -77,7 +77,10 @@ private: class Host: public Worker { friend class HostNodeTableHandler; + friend struct PeerHandshake; + friend struct RLPXHandshake; + friend class Session; friend class HostCapabilityFace; @@ -237,6 +240,8 @@ private: struct PeerSecrets { friend struct PeerHandshake; + friend struct RLPXHandshake; + protected: Secret encryptK; Secret macK; diff --git a/libp2p/Peer.h b/libp2p/Peer.h index 5c1aa015d..e9b45845b 100644 --- a/libp2p/Peer.h +++ b/libp2p/Peer.h @@ -54,7 +54,9 @@ class Peer: public Node { friend class Session; /// Allows Session to update score and rating. friend class Host; /// For Host: saveNetwork(), restoreNetwork() + friend struct PeerHandshake; + friend struct RLPXHandshake; public: bool isOffline() const { return !m_session.lock(); } From 53fda56158d2063b65ab8354048a6fe1a170ba4a Mon Sep 17 00:00:00 2001 From: subtly Date: Thu, 5 Mar 2015 00:22:35 +0100 Subject: [PATCH 025/100] test initial secrets and ciphers, interop with go --- libdevcrypto/ECDHE.cpp | 2 +- libdevcrypto/ECDHE.h | 6 +- libp2p/RLPxHandshake.cpp | 198 +++++++++---------- libp2p/RLPxHandshake.h | 29 +-- test/crypto.cpp | 93 +-------- test/rlpx.cpp | 417 +++++++++++++++++++++++++++++++++++++++ 6 files changed, 524 insertions(+), 221 deletions(-) create mode 100644 test/rlpx.cpp diff --git a/libdevcrypto/ECDHE.cpp b/libdevcrypto/ECDHE.cpp index 8f667f0f9..a00a92872 100644 --- a/libdevcrypto/ECDHE.cpp +++ b/libdevcrypto/ECDHE.cpp @@ -34,7 +34,7 @@ void dev::crypto::ecdh::agree(Secret const& _s, Public const& _r, h256& o_s) s_secp256k1.agree(_s, _r, o_s); } -void ECDHE::agree(Public const& _remote, Secret& o_sharedSecret) +void ECDHE::agree(Public const& _remote, Secret& o_sharedSecret) const { if (m_remoteEphemeral) // agreement can only occur once diff --git a/libdevcrypto/ECDHE.h b/libdevcrypto/ECDHE.h index 6d01b0329..d3c9ae325 100644 --- a/libdevcrypto/ECDHE.h +++ b/libdevcrypto/ECDHE.h @@ -70,11 +70,11 @@ public: Secret seckey() { return m_ephemeral.sec(); } /// Input public key for dh agreement, output generated shared secret. - void agree(Public const& _remoteEphemeral, Secret& o_sharedSecret); + void agree(Public const& _remoteEphemeral, Secret& o_sharedSecret) const; protected: - KeyPair m_ephemeral; ///< Ephemeral keypair; generated. - Public m_remoteEphemeral; ///< Public key of remote; parameter. + KeyPair m_ephemeral; ///< Ephemeral keypair; generated. + mutable Public m_remoteEphemeral; ///< Public key of remote; parameter. Set once when agree is called, otherwise immutable. }; /** diff --git a/libp2p/RLPxHandshake.cpp b/libp2p/RLPxHandshake.cpp index de4e9b90f..18db43577 100644 --- a/libp2p/RLPxHandshake.cpp +++ b/libp2p/RLPxHandshake.cpp @@ -28,7 +28,7 @@ using namespace dev; using namespace dev::p2p; using namespace CryptoPP; -RLPXFrameIO::RLPXFrameIO(bool _originated, Secret const& _ephemeralShared, bytesConstRef _authCipher, bytesConstRef _ackCipher): m_keys(h128(), h128()), m_macUpdateEncryptor(sha3("test").data(), 16) +RLPXFrameIO::RLPXFrameIO(RLPXHandshake& _init): m_macEnc() { // we need: // originated? @@ -39,37 +39,53 @@ RLPXFrameIO::RLPXFrameIO(bool _originated, Secret const& _ephemeralShared, bytes bytes keyMaterialBytes(512); bytesRef keyMaterial(&keyMaterialBytes); -// ecdhe.agree(remoteEphemeral, ess); - _ephemeralShared.ref().copyTo(keyMaterial.cropped(0, h256::size)); -// ss.ref().copyTo(keyMaterial.cropped(h256::size, h256::size)); -// // auto token = sha3(ssA); -// k->encryptK = sha3(keyMaterial); -// k->encryptK.ref().copyTo(keyMaterial.cropped(h256::size, h256::size)); -// k->macK = sha3(keyMaterial); -// -// // Initiator egress-mac: sha3(mac-secret^recipient-nonce || auth-sent-init) -// // ingress-mac: sha3(mac-secret^initiator-nonce || auth-recvd-ack) -// // Recipient egress-mac: sha3(mac-secret^initiator-nonce || auth-sent-ack) -// // ingress-mac: sha3(mac-secret^recipient-nonce || auth-recvd-init) -// -// bytes const& egressCipher = _originated ? authCipher : ackCipher; -// keyMaterialBytes.resize(h256::size + egressCipher.size()); -// keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); -// (k->macK ^ remoteNonce).ref().copyTo(keyMaterial); -// bytesConstRef(&egressCipher).copyTo(keyMaterial.cropped(h256::size, egressCipher.size())); -// k->egressMac = sha3(keyMaterial); -// -// bytes const& ingressCipher = _originated ? ackCipher : authCipher; -// keyMaterialBytes.resize(h256::size + ingressCipher.size()); -// keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); -// (k->macK ^ nonce).ref().copyTo(keyMaterial); -// bytesConstRef(&ingressCipher).copyTo(keyMaterial.cropped(h256::size, ingressCipher.size())); -// k->ingressMac = sha3(keyMaterial); + // shared-secret = sha3(ecdhe-shared-secret || sha3(nonce || initiator-nonce)) + Secret ephemeralShared; + _init.ecdhe.agree(_init.remoteEphemeral, ephemeralShared); + ephemeralShared.ref().copyTo(keyMaterial.cropped(0, h256::size)); + h512 nonceMaterial; + h256& leftNonce = _init.originated ? _init.remoteNonce : _init.nonce; + h256& rightNonce = _init.originated ? _init.nonce : _init.remoteNonce; + leftNonce.ref().copyTo(nonceMaterial.ref().cropped(0, h256::size)); + rightNonce.ref().copyTo(nonceMaterial.ref().cropped(h256::size, h256::size)); + auto outRef(keyMaterial.cropped(h256::size, h256::size)); + sha3(nonceMaterial.ref(), outRef); // output h(nonces) + + sha3(keyMaterial, outRef); // output shared-secret + // token: sha3(outRef) + + // aes-secret = sha3(ecdhe-shared-secret || shared-secret) + sha3(keyMaterial, outRef); // output aes-secret + m_frameEnc.SetKey(outRef.data(), h256::size); + // mac-secret = sha3(ecdhe-shared-secret || aes-secret) + sha3(keyMaterial, outRef); // output mac-secret + m_macEnc.SetKey(outRef.data(), h256::size); + + // Initiator egress-mac: sha3(mac-secret^recipient-nonce || auth-sent-init) + // ingress-mac: sha3(mac-secret^initiator-nonce || auth-recvd-ack) + // Recipient egress-mac: sha3(mac-secret^initiator-nonce || auth-sent-ack) + // ingress-mac: sha3(mac-secret^recipient-nonce || auth-recvd-init) + + (*(h256*)outRef.data() ^ _init.remoteNonce).ref().copyTo(keyMaterial); + bytes const& egressCipher = _init.originated ? _init.authCipher : _init.ackCipher; + keyMaterialBytes.resize(h256::size + egressCipher.size()); + keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); + bytesConstRef(&egressCipher).copyTo(keyMaterial.cropped(h256::size, egressCipher.size())); + m_egressMac.Update(keyMaterial.data(), keyMaterial.size()); + + // recover mac-secret by re-xoring remoteNonce + (*(h256*)outRef.data() ^ _init.remoteNonce ^ _init.nonce).ref().copyTo(keyMaterial); + bytes const& ingressCipher = _init.originated ? _init.ackCipher : _init.authCipher; + keyMaterialBytes.resize(h256::size + ingressCipher.size()); + keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); + bytesConstRef(&ingressCipher).copyTo(keyMaterial.cropped(h256::size, ingressCipher.size())); + m_ingressMac.Update(keyMaterial.data(), keyMaterial.size()); } void RLPXFrameIO::writeFullPacketFrame(bytesConstRef _packet) { + RLPStream header; } @@ -135,7 +151,7 @@ void RLPXFrameIO::updateMAC(SHA3_256& _mac, h128 const& _seed) prevDigest.TruncatedFinal(prevDigestOut.data(), h128::size); h128 encDigest; - m_macUpdateEncryptor.ProcessData(encDigest.data(), prevDigestOut.data(), h128::size); + m_macEnc.ProcessData(encDigest.data(), prevDigestOut.data(), h128::size); encDigest ^= (!!_seed ? _seed : prevDigestOut); // update mac for final digest @@ -152,8 +168,8 @@ void RLPXHandshake::generateAuth() bytesRef nonce(&auth[Signature::size + h256::size + Public::size], h256::size); // E(remote-pubk, S(ecdhe-random, ecdh-shared-secret^nonce) || H(ecdhe-random-pubk) || pubk || nonce || 0x0) - crypto::ecdh::agree(host->m_alias.sec(), remote, ss); - sign(ecdhe.seckey(), ss ^ this->nonce).ref().copyTo(sig); +//crypto::ecdh::agree(host->m_alias.sec(), remote, ss); +//sign(ecdhe.seckey(), ss ^ this->nonce).ref().copyTo(sig); sha3(ecdhe.pubkey().ref(), hepubk); host->m_alias.pub().ref().copyTo(pubk); this->nonce.ref().copyTo(nonce); @@ -178,8 +194,8 @@ bool RLPXHandshake::decodeAuth() pubk.copyTo(remote.ref()); nonce.copyTo(remoteNonce.ref()); - crypto::ecdh::agree(host->m_alias.sec(), remote, ss); - remoteEphemeral = recover(*(Signature*)sig.data(), ss ^ remoteNonce); +//crypto::ecdh::agree(host->m_alias.sec(), remote, ss); +//remoteEphemeral = recover(*(Signature*)sig.data(), ss ^ remoteNonce); assert(sha3(remoteEphemeral) == *(h256*)hepubk.data()); return true; } @@ -295,77 +311,49 @@ void RLPXHandshake::transition(boost::system::error_code _ech) clog(NetConnect) << "p2p.connect.egress sending magic sequence"; else clog(NetConnect) << "p2p.connect.ingress sending magic sequence"; - PeerSecrets* k = new PeerSecrets; - bytes keyMaterialBytes(512); - bytesRef keyMaterial(&keyMaterialBytes); - - ecdhe.agree(remoteEphemeral, ess); - ess.ref().copyTo(keyMaterial.cropped(0, h256::size)); - ss.ref().copyTo(keyMaterial.cropped(h256::size, h256::size)); - // auto token = sha3(ssA); - k->encryptK = sha3(keyMaterial); - k->encryptK.ref().copyTo(keyMaterial.cropped(h256::size, h256::size)); - k->macK = sha3(keyMaterial); - - // Initiator egress-mac: sha3(mac-secret^recipient-nonce || auth-sent-init) - // ingress-mac: sha3(mac-secret^initiator-nonce || auth-recvd-ack) - // Recipient egress-mac: sha3(mac-secret^initiator-nonce || auth-sent-ack) - // ingress-mac: sha3(mac-secret^recipient-nonce || auth-recvd-init) - bytes const& egressCipher = originated ? authCipher : ackCipher; - keyMaterialBytes.resize(h256::size + egressCipher.size()); - keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); - (k->macK ^ remoteNonce).ref().copyTo(keyMaterial); - bytesConstRef(&egressCipher).copyTo(keyMaterial.cropped(h256::size, egressCipher.size())); - k->egressMac = sha3(keyMaterial); + RLPXFrameIO* io = new RLPXFrameIO(*this); - bytes const& ingressCipher = originated ? ackCipher : authCipher; - keyMaterialBytes.resize(h256::size + ingressCipher.size()); - keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); - (k->macK ^ nonce).ref().copyTo(keyMaterial); - bytesConstRef(&ingressCipher).copyTo(keyMaterial.cropped(h256::size, ingressCipher.size())); - k->ingressMac = sha3(keyMaterial); - - // This test will be replaced with protocol-capabilities information (was Hello packet) - // TESTING: send encrypt magic sequence - bytes magic {0x22,0x40,0x08,0x91}; - // rlpx encrypt - encryptSymNoAuth(k->encryptK, &magic, k->magicCipherAndMac, h128()); - k->magicCipherAndMac.resize(k->magicCipherAndMac.size() + 32); - sha3mac(k->egressMac.ref(), &magic, k->egressMac.ref()); - k->egressMac.ref().copyTo(bytesRef(&k->magicCipherAndMac).cropped(k->magicCipherAndMac.size() - 32, 32)); - - clog(NetConnect) << "p2p.connect.egress txrx magic sequence"; - k->recvdMagicCipherAndMac.resize(k->magicCipherAndMac.size()); - - ba::async_write(*socket, ba::buffer(k->magicCipherAndMac), [this, self, k, magic](boost::system::error_code ec, std::size_t) - { - if (ec) - { - delete k; - transition(ec); - return; - } - - ba::async_read(*socket, ba::buffer(k->recvdMagicCipherAndMac, k->magicCipherAndMac.size()), [this, self, k, magic](boost::system::error_code ec, std::size_t) - { - if (originated) - clog(NetNote) << "p2p.connect.egress recving magic sequence"; - else - clog(NetNote) << "p2p.connect.ingress recving magic sequence"; - - if (ec) - { - delete k; - transition(ec); - return; - } - - /// capabilities handshake (encrypted magic sequence is placeholder) - bytes decryptedMagic; - decryptSymNoAuth(k->encryptK, h128(), &k->recvdMagicCipherAndMac, decryptedMagic); - if (decryptedMagic[0] == 0x22 && decryptedMagic[1] == 0x40 && decryptedMagic[2] == 0x08 && decryptedMagic[3] == 0x91) - { +// // This test will be replaced with protocol-capabilities information (was Hello packet) +// // TESTING: send encrypt magic sequence +// bytes magic {0x22,0x40,0x08,0x91}; +// // rlpx encrypt +// encryptSymNoAuth(k->encryptK, &magic, k->magicCipherAndMac, h128()); +// k->magicCipherAndMac.resize(k->magicCipherAndMac.size() + 32); +// sha3mac(k->egressMac.ref(), &magic, k->egressMac.ref()); +// k->egressMac.ref().copyTo(bytesRef(&k->magicCipherAndMac).cropped(k->magicCipherAndMac.size() - 32, 32)); +// +// clog(NetConnect) << "p2p.connect.egress txrx magic sequence"; +// k->recvdMagicCipherAndMac.resize(k->magicCipherAndMac.size()); +// +// ba::async_write(*socket, ba::buffer(k->magicCipherAndMac), [this, self, k, magic](boost::system::error_code ec, std::size_t) +// { +// if (ec) +// { +// delete k; +// transition(ec); +// return; +// } +// +// ba::async_read(*socket, ba::buffer(k->recvdMagicCipherAndMac, k->magicCipherAndMac.size()), [this, self, k, magic](boost::system::error_code ec, std::size_t) +// { +// if (originated) +// clog(NetNote) << "p2p.connect.egress recving magic sequence"; +// else +// clog(NetNote) << "p2p.connect.ingress recving magic sequence"; +// +// if (ec) +// { +// delete k; +// transition(ec); +// return; +// } +// +// /// capabilities handshake (encrypted magic sequence is placeholder) +// bytes decryptedMagic; +// decryptSymNoAuth(k->encryptK, h128(), &k->recvdMagicCipherAndMac, decryptedMagic); +// if (decryptedMagic[0] == 0x22 && decryptedMagic[1] == 0x40 && decryptedMagic[2] == 0x08 && decryptedMagic[3] == 0x91) +// { shared_ptr p; p = host->m_peers[remote]; if (!p) @@ -380,11 +368,11 @@ void RLPXHandshake::transition(boost::system::error_code _ech) auto ps = std::make_shared(host, move(*socket), p); ps->start(); - } +// } // todo: PeerSession will take ownership of k and use it to encrypt wireline. - delete k; - }); - }); + delete io; +// }); +// }); } } diff --git a/libp2p/RLPxHandshake.h b/libp2p/RLPxHandshake.h index 0e39dc810..a0b739660 100644 --- a/libp2p/RLPxHandshake.h +++ b/libp2p/RLPxHandshake.h @@ -34,11 +34,13 @@ namespace dev { namespace p2p { - + +class RLPXHandshake; + class RLPXFrameIO { public: - RLPXFrameIO(bool _originated, Secret const& _ephemeralShared, bytesConstRef _authCipher, bytesConstRef _ackCipher); + RLPXFrameIO(RLPXHandshake& _init); void writeFullPacketFrame(bytesConstRef _packet); @@ -61,29 +63,17 @@ public: void updateIngressMACWithEndOfFrame(bytesConstRef _cipher); private: - struct RLPXSecrets - { - // ideally this will be passed ecdhe-shared-secret, auth cipher, ack cpiher - RLPXSecrets(h128 const& encK, h128 const& macK): frameEnc(encK.data(), h128::size), macEnc(macK.data(), h128::size) {} - - CryptoPP::CTR_Mode::Encryption frameEnc; - CryptoPP::ECB_Mode::Encryption macEnc; - - CryptoPP::SHA3_256 egressMac; - CryptoPP::SHA3_256 ingressMac; - }; - void updateMAC(CryptoPP::SHA3_256& _mac, h128 const& _seed = h128()); - CryptoPP::ECB_Mode::Encryption m_macUpdateEncryptor; + CryptoPP::CTR_Mode::Encryption m_frameEnc; + CryptoPP::ECB_Mode::Encryption m_macEnc; CryptoPP::SHA3_256 m_egressMac; CryptoPP::SHA3_256 m_ingressMac; - - RLPXSecrets m_keys; }; struct RLPXHandshake: public std::enable_shared_from_this { + friend class RLPXFrameIO; friend class Host; enum State { @@ -131,10 +121,7 @@ private: bytes authCipher; bytes ack; bytes ackCipher; - - Secret ss; - Secret ess; - + crypto::ECDHE ecdhe; h256 nonce; diff --git a/test/crypto.cpp b/test/crypto.cpp index ebe8db6c5..c20b4c51f 100644 --- a/test/crypto.cpp +++ b/test/crypto.cpp @@ -15,6 +15,7 @@ along with cpp-ethereum. If not, see . */ /** @file crypto.cpp + * @author Alex Leverington * @author Gav Wood * @date 2014 * Crypto test functions. @@ -228,7 +229,7 @@ BOOST_AUTO_TEST_CASE(cryptopp_ecdsa_sipaseckp256k1) } } -BOOST_AUTO_TEST_CASE(rlpx_sha3_norestart) +BOOST_AUTO_TEST_CASE(sha3_norestart) { CryptoPP::SHA3_256 ctx; bytes input(asBytes("test")); @@ -259,96 +260,6 @@ BOOST_AUTO_TEST_CASE(rlpx_sha3_norestart) BOOST_REQUIRE(finalDigest2 != finalDigest3); } -BOOST_AUTO_TEST_CASE(rlpx_updatemac_aesecb_sha3) -{ - -} - -BOOST_AUTO_TEST_CASE(ecies_interop_test) -{ - CryptoPP::SHA256 sha256ctx; - bytes emptyExpected(fromHex("0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")); - bytes empty; - sha256ctx.Update(empty.data(), 0); - bytes emptyTestOut(32); - sha256ctx.Final(emptyTestOut.data()); - BOOST_REQUIRE(emptyExpected == emptyTestOut); - - bytes hash1Expected(fromHex("0x8949b278bbafb8da1aaa18cb724175c5952280f74be5d29ab4b37d1b45c84b08")); - bytes hash1input(fromHex("0x55a53b55afb12affff3c")); - sha256ctx.Update(hash1input.data(), hash1input.size()); - bytes hash1Out(32); - sha256ctx.Final(hash1Out.data()); - BOOST_REQUIRE(hash1Out == hash1Expected); - - h128 hmack(fromHex("0x07a4b6dfa06369a570f2dcba2f11a18f")); - CryptoPP::HMAC hmacctx(hmack.data(), h128::size); - bytes input(fromHex("0x4dcb92ed4fc67fe86832")); - hmacctx.Update(input.data(), input.size()); - bytes hmacExpected(fromHex("0xc90b62b1a673b47df8e395e671a68bfa68070d6e2ef039598bb829398b89b9a9")); - bytes hmacOut(hmacExpected.size()); - hmacctx.Final(hmacOut.data()); - BOOST_REQUIRE(hmacExpected == hmacOut); - - // go messageTag - bytes tagSecret(fromHex("0xaf6623e52208c596e17c72cea6f1cb09")); - bytes tagInput(fromHex("0x3461282bcedace970df2")); - bytes tagExpected(fromHex("0xb3ce623bce08d5793677ba9441b22bb34d3e8a7de964206d26589df3e8eb5183")); - CryptoPP::HMAC hmactagctx(tagSecret.data(), tagSecret.size()); - hmactagctx.Update(tagInput.data(), tagInput.size()); - h256 mac; - hmactagctx.Final(mac.data()); - BOOST_REQUIRE(mac.asBytes() == tagExpected); - - Secret input1(fromHex("0x0de72f1223915fa8b8bf45dffef67aef8d89792d116eb61c9a1eb02c422a4663")); - bytes expect1(fromHex("0x1d0c446f9899a3426f2b89a8cb75c14b")); - bytes test1; - test1 = s_secp256k1.eciesKDF(input1, bytes(), 16); - BOOST_REQUIRE(test1 == expect1); - - Secret kdfInput2(fromHex("0x961c065873443014e0371f1ed656c586c6730bf927415757f389d92acf8268df")); - bytes kdfExpect2(fromHex("0x4050c52e6d9c08755e5a818ac66fabe478b825b1836fd5efc4d44e40d04dabcc")); - bytes kdfTest2; - kdfTest2 = s_secp256k1.eciesKDF(kdfInput2, bytes(), 32); - BOOST_REQUIRE(kdfTest2 == kdfExpect2); - - KeyPair k(Secret(fromHex("0x332143e9629eedff7d142d741f896258f5a1bfab54dab2121d3ec5000093d74b"))); - Public p(fromHex("0xf0d2b97981bd0d415a843b5dfe8ab77a30300daab3658c578f2340308a2da1a07f0821367332598b6aa4e180a41e92f4ebbae3518da847f0b1c0bbfe20bcf4e1")); - Secret agreeExpected(fromHex("0xee1418607c2fcfb57fda40380e885a707f49000a5dda056d828b7d9bd1f29a08")); - Secret agreeTest; - s_secp256k1.agree(k.sec(), p, agreeTest); - BOOST_REQUIRE(agreeExpected == agreeTest); - - KeyPair kmK(Secret(fromHex("0x57baf2c62005ddec64c357d96183ebc90bf9100583280e848aa31d683cad73cb"))); - bytes kmCipher(fromHex("0x04ff2c874d0a47917c84eea0b2a4141ca95233720b5c70f81a8415bae1dc7b746b61df7558811c1d6054333907333ef9bb0cc2fbf8b34abb9730d14e0140f4553f4b15d705120af46cf653a1dc5b95b312cf8444714f95a4f7a0425b67fc064d18f4d0a528761565ca02d97faffdac23de10")); - bytes kmPlain = kmCipher; - bytes kmExpected(asBytes("a")); - BOOST_REQUIRE(s_secp256k1.decryptECIES(kmK.sec(), kmPlain)); - BOOST_REQUIRE(kmExpected == kmPlain); - - KeyPair kenc(Secret(fromHex("0x472413e97f1fd58d84e28a559479e6b6902d2e8a0cee672ef38a3a35d263886b"))); - Public penc(Public(fromHex("0x7a2aa2951282279dc1171549a7112b07c38c0d97c0fe2c0ae6c4588ba15be74a04efc4f7da443f6d61f68a9279bc82b73e0cc8d090048e9f87e838ae65dd8d4c"))); - BOOST_REQUIRE(penc == kenc.pub()); - - bytes cipher1(fromHex("0x046f647e1bd8a5cd1446d31513bac233e18bdc28ec0e59d46de453137a72599533f1e97c98154343420d5f16e171e5107999a7c7f1a6e26f57bcb0d2280655d08fb148d36f1d4b28642d3bb4a136f0e33e3dd2e3cffe4b45a03fb7c5b5ea5e65617250fdc89e1a315563c20504b9d3a72555")); - bytes plainTest1 = cipher1; - bytes expectedPlain1 = asBytes("a"); - BOOST_REQUIRE(s_secp256k1.decryptECIES(kenc.sec(), plainTest1)); - BOOST_REQUIRE(plainTest1 == expectedPlain1); - - bytes cipher2(fromHex("0x0443c24d6ccef3ad095140760bb143078b3880557a06392f17c5e368502d79532bc18903d59ced4bbe858e870610ab0d5f8b7963dd5c9c4cf81128d10efd7c7aa80091563c273e996578403694673581829e25a865191bdc9954db14285b56eb0043b6288172e0d003c10f42fe413222e273d1d4340c38a2d8344d7aadcbc846ee")); - bytes plainTest2 = cipher2; - bytes expectedPlain2 = asBytes("aaaaaaaaaaaaaaaa"); - BOOST_REQUIRE(s_secp256k1.decryptECIES(kenc.sec(), plainTest2)); - BOOST_REQUIRE(plainTest2 == expectedPlain2); - - bytes cipher3(fromHex("0x04c4e40c86bb5324e017e598c6d48c19362ae527af8ab21b077284a4656c8735e62d73fb3d740acefbec30ca4c024739a1fcdff69ecaf03301eebf156eb5f17cca6f9d7a7e214a1f3f6e34d1ee0ec00ce0ef7d2b242fbfec0f276e17941f9f1bfbe26de10a15a6fac3cda039904ddd1d7e06e7b96b4878f61860e47f0b84c8ceb64f6a900ff23844f4359ae49b44154980a626d3c73226c19e")); - bytes plainTest3 = cipher3; - bytes expectedPlain3 = asBytes("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); - BOOST_REQUIRE(s_secp256k1.decryptECIES(kenc.sec(), plainTest3)); - BOOST_REQUIRE(plainTest3 == expectedPlain3); -} - BOOST_AUTO_TEST_CASE(ecies_kdf) { KeyPair local = KeyPair::create(); diff --git a/test/rlpx.cpp b/test/rlpx.cpp new file mode 100644 index 000000000..d845911f4 --- /dev/null +++ b/test/rlpx.cpp @@ -0,0 +1,417 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** @file crypto.cpp + * @author Alex Leverington + * @date 2015 + * RLPx test functions. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace dev; +using namespace dev::crypto; +using namespace CryptoPP; + +BOOST_AUTO_TEST_SUITE(rlpx) + +static Secp256k1 s_secp256k1; +static CryptoPP::AutoSeededRandomPool s_rng; +static CryptoPP::OID s_curveOID(CryptoPP::ASN1::secp256k1()); +static CryptoPP::DL_GroupParameters_EC s_params(s_curveOID); +static CryptoPP::DL_GroupParameters_EC::EllipticCurve s_curve(s_params.GetCurve()); + +BOOST_AUTO_TEST_CASE(test_secrets_cpp_vectors) +{ + KeyPair init(Secret(sha3("initiator"))); + KeyPair initR(Secret(sha3("initiator-random"))); + h256 initNonce(sha3("initiator-nonce")); + + KeyPair recv(Secret(sha3("remote-recv"))); + KeyPair recvR(Secret(sha3("remote-recv-random"))); + h256 recvNonce(sha3("remote-recv-nonce")); + + bytes authCipher(fromHex("")); + bytes ackCipher(fromHex("")); + + CryptoPP::CTR_Mode::Encryption m_frameEnc; + CryptoPP::CTR_Mode::Encryption m_frameDec; + CryptoPP::ECB_Mode::Encryption m_macEnc; + CryptoPP::SHA3_256 m_egressMac; + CryptoPP::SHA3_256 m_ingressMac; + + // when originated is true, agreement is with init secrets + // when originated is true, remoteNonce = recvNonce + // when originated is true, nonce = initNonce + bool originated = true; + auto remoteNonce = recvNonce; + auto nonce = initNonce; + bytes keyMaterialBytes(64); + bytesRef keyMaterial(&keyMaterialBytes); + + // shared-secret = sha3(ecdhe-shared-secret || sha3(nonce || initiator-nonce)) + Secret ephemeralShared; + s_secp256k1.agree(initR.sec(), recvR.pub(), ephemeralShared); + Secret expected(fromHex("20d82c1092f351dc217bd66fa183e801234af14ead40423b6ee25112201c6e5a")); + BOOST_REQUIRE(expected == ephemeralShared); + + ephemeralShared.ref().copyTo(keyMaterial.cropped(0, h256::size)); + h512 nonceMaterial; + h256 const& leftNonce = originated ? remoteNonce : nonce; + h256 const& rightNonce = originated ? nonce : remoteNonce; + leftNonce.ref().copyTo(nonceMaterial.ref().cropped(0, h256::size)); + rightNonce.ref().copyTo(nonceMaterial.ref().cropped(h256::size, h256::size)); + auto outRef(keyMaterial.cropped(h256::size, h256::size)); + sha3(nonceMaterial.ref(), outRef); // output h(nonces) + + // test that keyMaterial = ecdhe-shared-secret || sha3(nonce || initiator-nonce) + { + BOOST_REQUIRE(ephemeralShared == *(Secret*)keyMaterialBytes.data()); + + SHA3_256 ctx; + ctx.Update(leftNonce.data(), h256::size); + ctx.Update(rightNonce.data(), h256::size); + bytes expected(32); + ctx.Final(expected.data()); + bytes given(32); + outRef.copyTo(&given); + BOOST_REQUIRE(expected == given); + } + bytes preImage(keyMaterialBytes); + + // shared-secret <- sha3(ecdhe-shared-secret || sha3(nonce || initiator-nonce)) + // keyMaterial = ecdhe-shared-secret || shared-secret + sha3(keyMaterial, outRef); + bytes sharedSecret(32); + outRef.copyTo(&sharedSecret); + BOOST_REQUIRE(sharedSecret == fromHex("b65319ce56e00f3be75c4d0da92b5957d5583ca25eeeedac8e29b6dfc8b1ddf7")); + + // test that keyMaterial = ecdhe-shared-secret || shared-secret + { + BOOST_REQUIRE(ephemeralShared == *(Secret*)keyMaterialBytes.data()); + + SHA3_256 ctx; + ctx.Update(preImage.data(), preImage.size()); + bytes expected(32); + ctx.Final(expected.data()); + bytes test(32); + outRef.copyTo(&test); + BOOST_REQUIRE(expected == test); + } + + // token: sha3(outRef) + bytes token(32); + sha3(outRef, bytesRef(&token)); + BOOST_REQUIRE(token == fromHex("db41fe0180f372983cf19fca7ee890f7fb5481079d44683d2c027be9e71bbca2")); + + // aes-secret = sha3(ecdhe-shared-secret || shared-secret) + sha3(keyMaterial, outRef); // output aes-secret + bytes aesSecret(32); + outRef.copyTo(&aesSecret); + BOOST_REQUIRE(aesSecret == fromHex("12347b4784bcb4e74b84637940482852fe25d78e328cf5c6f7a396bf96cc20bb")); + m_frameEnc.SetKeyWithIV(outRef.data(), h128::size, h128().data()); + m_frameDec.SetKeyWithIV(outRef.data(), h128::size, h128().data()); + + // mac-secret = sha3(ecdhe-shared-secret || aes-secret) + sha3(keyMaterial, outRef); // output mac-secret + bytes macSecret(32); + outRef.copyTo(&macSecret); + BOOST_REQUIRE(macSecret == fromHex("2ec149072353d54437422837c886b0538a9206e6c559f6b4a55f65a866867723")); + m_macEnc.SetKey(outRef.data(), h256::size); + + // Initiator egress-mac: sha3(mac-secret^recipient-nonce || auth-sent-init) + // ingress-mac: sha3(mac-secret^initiator-nonce || auth-recvd-ack) + // Recipient egress-mac: sha3(mac-secret^initiator-nonce || auth-sent-ack) + // ingress-mac: sha3(mac-secret^recipient-nonce || auth-recvd-init) + + (*(h256*)outRef.data() ^ remoteNonce).ref().copyTo(keyMaterial); + bytes const& egressCipher = originated ? authCipher : ackCipher; + keyMaterialBytes.resize(h256::size + egressCipher.size()); + keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); + bytesConstRef(&egressCipher).copyTo(keyMaterial.cropped(h256::size, egressCipher.size())); + m_egressMac.Update(keyMaterial.data(), keyMaterial.size()); + + { + bytes egressMac; + SHA3_256 h(m_egressMac); + bytes digest(16); + h.TruncatedFinal(digest.data(), 16); + BOOST_REQUIRE(digest == fromHex("23e5e8efb6e3765ecae1fca9160b18df")); + } + + // recover mac-secret by re-xoring remoteNonce + (*(h256*)keyMaterial.data() ^ remoteNonce ^ nonce).ref().copyTo(keyMaterial); + bytes const& ingressCipher = originated ? ackCipher : authCipher; + keyMaterialBytes.resize(h256::size + ingressCipher.size()); + keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); + bytesConstRef(&ingressCipher).copyTo(keyMaterial.cropped(h256::size, ingressCipher.size())); + m_ingressMac.Update(keyMaterial.data(), keyMaterial.size()); + + { + bytes ingressMac; + SHA3_256 h(m_ingressMac); + bytes digest(16); + h.TruncatedFinal(digest.data(), 16); + BOOST_REQUIRE(digest == fromHex("ceed64135852064cbdde86e7ea05e8f5")); + } +} + +BOOST_AUTO_TEST_CASE(test_secrets_from_go) +{ + KeyPair init(Secret(fromHex("0x5e173f6ac3c669587538e7727cf19b782a4f2fda07c1eaa662c593e5e85e3051"))); + KeyPair initR(Secret(fromHex("0x19c2185f4f40634926ebed3af09070ca9e029f2edd5fae6253074896205f5f6c"))); + h256 initNonce(fromHex("0xcd26fecb93657d1cd9e9eaf4f8be720b56dd1d39f190c4e1c6b7ec66f077bb11")); + + KeyPair recv(Secret(fromHex("0xc45f950382d542169ea207959ee0220ec1491755abe405cd7498d6b16adb6df8"))); + KeyPair recvR(Secret(fromHex("0xd25688cf0ab10afa1a0e2dba7853ed5f1e5bf1c631757ed4e103b593ff3f5620"))); + h256 recvNonce(fromHex("0xf37ec61d84cea03dcc5e8385db93248584e8af4b4d1c832d8c7453c0089687a7")); + + bytes authCipher(fromHex("0x04a0274c5951e32132e7f088c9bdfdc76c9d91f0dc6078e848f8e3361193dbdc43b94351ea3d89e4ff33ddcefbc80070498824857f499656c4f79bbd97b6c51a514251d69fd1785ef8764bd1d262a883f780964cce6a14ff206daf1206aa073a2d35ce2697ebf3514225bef186631b2fd2316a4b7bcdefec8d75a1025ba2c5404a34e7795e1dd4bc01c6113ece07b0df13b69d3ba654a36e35e69ff9d482d88d2f0228e7d96fe11dccbb465a1831c7d4ad3a026924b182fc2bdfe016a6944312021da5cc459713b13b86a686cf34d6fe6615020e4acf26bf0d5b7579ba813e7723eb95b3cef9942f01a58bd61baee7c9bdd438956b426a4ffe238e61746a8c93d5e10680617c82e48d706ac4953f5e1c4c4f7d013c87d34a06626f498f34576dc017fdd3d581e83cfd26cf125b6d2bda1f1d56")); + bytes ackCipher(fromHex("0x049934a7b2d7f9af8fd9db941d9da281ac9381b5740e1f64f7092f3588d4f87f5ce55191a6653e5e80c1c5dd538169aa123e70dc6ffc5af1827e546c0e958e42dad355bcc1fcb9cdf2cf47ff524d2ad98cbf275e661bf4cf00960e74b5956b799771334f426df007350b46049adb21a6e78ab1408d5e6ccde6fb5e69f0f4c92bb9c725c02f99fa72b9cdc8dd53cff089e0e73317f61cc5abf6152513cb7d833f09d2851603919bf0fbe44d79a09245c6e8338eb502083dc84b846f2fee1cc310d2cc8b1b9334728f97220bb799376233e113")); + + bytes authPlainExpected(fromHex("0x884c36f7ae6b406637c1f61b2f57e1d2cab813d24c6559aaf843c3f48962f32f46662c066d39669b7b2e3ba14781477417600e7728399278b1b5d801a519aa570034fdb5419558137e0d44cd13d319afe5629eeccb47fd9dfe55cc6089426e46cc762dd8a0636e07a54b31169eba0c7a20a1ac1ef68596f1f283b5c676bae4064abfcce24799d09f67e392632d3ffdc12e3d6430dcb0ea19c318343ffa7aae74d4cd26fecb93657d1cd9e9eaf4f8be720b56dd1d39f190c4e1c6b7ec66f077bb1100")); + bytes ackPlainExpected(fromHex("0x802b052f8b066640bba94a4fc39d63815c377fced6fcb84d27f791c9921ddf3e9bf0108e298f490812847109cbd778fae393e80323fd643209841a3b7f110397f37ec61d84cea03dcc5e8385db93248584e8af4b4d1c832d8c7453c0089687a700")); + + bytes authPlain = authCipher; + BOOST_REQUIRE(s_secp256k1.decryptECIES(recv.sec(), authPlain)); + bytes ackPlain = ackCipher; + BOOST_REQUIRE(s_secp256k1.decryptECIES(init.sec(), ackPlain)); + + CryptoPP::CTR_Mode::Encryption m_frameEnc; + CryptoPP::CTR_Mode::Encryption m_frameDec; + CryptoPP::ECB_Mode::Encryption m_macEnc; + CryptoPP::SHA3_256 m_egressMac; + CryptoPP::SHA3_256 m_ingressMac; + + // when originated is true, agreement is with init secrets + // when originated is true, remoteNonce = recvNonce + // when originated is true, nonce = initNonce + bool originated = true; + auto remoteNonce = recvNonce; + auto nonce = initNonce; + bytes keyMaterialBytes(64); + bytesRef keyMaterial(&keyMaterialBytes); + + // shared-secret = sha3(ecdhe-shared-secret || sha3(nonce || initiator-nonce)) + Secret ephemeralShared; + s_secp256k1.agree(initR.sec(), recvR.pub(), ephemeralShared); + Secret expected(fromHex("0xe3f407f83fc012470c26a93fdff534100f2c6f736439ce0ca90e9914f7d1c381")); + BOOST_REQUIRE(expected == ephemeralShared); + + ephemeralShared.ref().copyTo(keyMaterial.cropped(0, h256::size)); + h512 nonceMaterial; + h256 const& leftNonce = originated ? remoteNonce : nonce; + h256 const& rightNonce = originated ? nonce : remoteNonce; + leftNonce.ref().copyTo(nonceMaterial.ref().cropped(0, h256::size)); + rightNonce.ref().copyTo(nonceMaterial.ref().cropped(h256::size, h256::size)); + auto outRef(keyMaterial.cropped(h256::size, h256::size)); + sha3(nonceMaterial.ref(), outRef); // output h(nonces) + + // test that keyMaterial = ecdhe-shared-secret || sha3(nonce || initiator-nonce) + { + BOOST_REQUIRE(ephemeralShared == *(Secret*)keyMaterialBytes.data()); + + SHA3_256 ctx; + ctx.Update(leftNonce.data(), h256::size); + ctx.Update(rightNonce.data(), h256::size); + bytes expected(32); + ctx.Final(expected.data()); + bytes given(32); + outRef.copyTo(&given); + BOOST_REQUIRE(expected == given); + } + bytes preImage(keyMaterialBytes); + + // shared-secret <- sha3(ecdhe-shared-secret || sha3(nonce || initiator-nonce)) + // keyMaterial = ecdhe-shared-secret || shared-secret + sha3(keyMaterial, outRef); + + // test that keyMaterial = ecdhe-shared-secret || shared-secret + { + BOOST_REQUIRE(ephemeralShared == *(Secret*)keyMaterialBytes.data()); + + SHA3_256 ctx; + ctx.Update(preImage.data(), preImage.size()); + bytes expected(32); + ctx.Final(expected.data()); + bytes test(32); + outRef.copyTo(&test); + BOOST_REQUIRE(expected == test); + } + + // token: sha3(outRef) + bytes token(32); + sha3(outRef, bytesRef(&token)); + BOOST_REQUIRE(token == fromHex("0x3f9ec2592d1554852b1f54d228f042ed0a9310ea86d038dc2b401ba8cd7fdac4")); + + // aes-secret = sha3(ecdhe-shared-secret || shared-secret) + sha3(keyMaterial, outRef); // output aes-secret + bytes aesSecret(32); + outRef.copyTo(&aesSecret); + BOOST_REQUIRE(aesSecret == fromHex("0xc0458fa97a5230830e05f4f20b7c755c1d4e54b1ce5cf43260bb191eef4e418d")); + m_frameEnc.SetKeyWithIV(outRef.data(), h128::size, h128().data()); + m_frameDec.SetKeyWithIV(outRef.data(), h128::size, h128().data()); + + // mac-secret = sha3(ecdhe-shared-secret || aes-secret) + sha3(keyMaterial, outRef); // output mac-secret + bytes macSecret(32); + outRef.copyTo(&macSecret); + BOOST_REQUIRE(macSecret == fromHex("0x48c938884d5067a1598272fcddaa4b833cd5e7d92e8228c0ecdfabbe68aef7f1")); + m_macEnc.SetKey(outRef.data(), h256::size); + + // Initiator egress-mac: sha3(mac-secret^recipient-nonce || auth-sent-init) + // ingress-mac: sha3(mac-secret^initiator-nonce || auth-recvd-ack) + // Recipient egress-mac: sha3(mac-secret^initiator-nonce || auth-sent-ack) + // ingress-mac: sha3(mac-secret^recipient-nonce || auth-recvd-init) + + (*(h256*)outRef.data() ^ remoteNonce).ref().copyTo(keyMaterial); + bytes const& egressCipher = originated ? authCipher : ackCipher; + keyMaterialBytes.resize(h256::size + egressCipher.size()); + keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); + bytesConstRef(&egressCipher).copyTo(keyMaterial.cropped(h256::size, egressCipher.size())); + m_egressMac.Update(keyMaterialBytes.data(), keyMaterialBytes.size()); + + { + bytes egressMac; + SHA3_256 h(m_egressMac); + bytes digest(32); + h.Final(digest.data()); + BOOST_REQUIRE(digest == fromHex("0x09771e93b1a6109e97074cbe2d2b0cf3d3878efafe68f53c41bb60c0ec49097e")); + } + + // recover mac-secret by re-xoring remoteNonce + bytes recoverMacSecretTest(32); + (*(h256*)keyMaterial.data() ^ remoteNonce).ref().copyTo(&recoverMacSecretTest); + BOOST_REQUIRE(recoverMacSecretTest == macSecret); + + (*(h256*)keyMaterial.data() ^ remoteNonce ^ nonce).ref().copyTo(keyMaterial); + bytes const& ingressCipher = originated ? ackCipher : authCipher; + keyMaterialBytes.resize(h256::size + ingressCipher.size()); + keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); + bytesConstRef(&ingressCipher).copyTo(keyMaterial.cropped(h256::size, ingressCipher.size())); + m_ingressMac.Update(keyMaterial.data(), keyMaterial.size()); + + { + bytes ingressMac; + SHA3_256 h(m_ingressMac); + bytes digest(32); + h.Final(digest.data()); + BOOST_CHECK(digest == fromHex("0x75823d96e23136c89666ee025fb21a432be906512b3dd4a3049e898adb433847")); + } + +// bytes initHello(fromHex("6ef23fcf1cec7312df623f9ae701e63b550cdb8517fefd8dd398fc2acd1d935e6e0434a2b96769078477637347b7b01924fff9ff1c06df2f804df3b0402bbb9f87365b3c6856b45e1e2b6470986813c3816a71bff9d69dd297a5dbd935ab578f6e5d7e93e4506a44f307c332d95e8a4b102585fd8ef9fc9e3e055537a5cec2e9")); +// +// bytes recvHello(fromHex("6ef23fcf1cec7312df623f9ae701e63be36a1cdd1b19179146019984f3625d4a6e0434a2b96769050577657247b7b02bc6c314470eca7e3ef650b98c83e9d7dd4830b3f718ff562349aead2530a8d28a8484604f92e5fced2c6183f304344ab0e7c301a0c05559f4c25db65e36820b4b909a226171a60ac6cb7beea09376d6d8")); +} + +BOOST_AUTO_TEST_CASE(ecies_interop_test_primitives) +{ + CryptoPP::SHA256 sha256ctx; + bytes emptyExpected(fromHex("0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")); + bytes empty; + sha256ctx.Update(empty.data(), 0); + bytes emptyTestOut(32); + sha256ctx.Final(emptyTestOut.data()); + BOOST_REQUIRE(emptyExpected == emptyTestOut); + + bytes hash1Expected(fromHex("0x8949b278bbafb8da1aaa18cb724175c5952280f74be5d29ab4b37d1b45c84b08")); + bytes hash1input(fromHex("0x55a53b55afb12affff3c")); + sha256ctx.Update(hash1input.data(), hash1input.size()); + bytes hash1Out(32); + sha256ctx.Final(hash1Out.data()); + BOOST_REQUIRE(hash1Out == hash1Expected); + + h128 hmack(fromHex("0x07a4b6dfa06369a570f2dcba2f11a18f")); + CryptoPP::HMAC hmacctx(hmack.data(), h128::size); + bytes input(fromHex("0x4dcb92ed4fc67fe86832")); + hmacctx.Update(input.data(), input.size()); + bytes hmacExpected(fromHex("0xc90b62b1a673b47df8e395e671a68bfa68070d6e2ef039598bb829398b89b9a9")); + bytes hmacOut(hmacExpected.size()); + hmacctx.Final(hmacOut.data()); + BOOST_REQUIRE(hmacExpected == hmacOut); + + // go messageTag + bytes tagSecret(fromHex("0xaf6623e52208c596e17c72cea6f1cb09")); + bytes tagInput(fromHex("0x3461282bcedace970df2")); + bytes tagExpected(fromHex("0xb3ce623bce08d5793677ba9441b22bb34d3e8a7de964206d26589df3e8eb5183")); + CryptoPP::HMAC hmactagctx(tagSecret.data(), tagSecret.size()); + hmactagctx.Update(tagInput.data(), tagInput.size()); + h256 mac; + hmactagctx.Final(mac.data()); + BOOST_REQUIRE(mac.asBytes() == tagExpected); + + Secret input1(fromHex("0x0de72f1223915fa8b8bf45dffef67aef8d89792d116eb61c9a1eb02c422a4663")); + bytes expect1(fromHex("0x1d0c446f9899a3426f2b89a8cb75c14b")); + bytes test1; + test1 = s_secp256k1.eciesKDF(input1, bytes(), 16); + BOOST_REQUIRE(test1 == expect1); + + Secret kdfInput2(fromHex("0x961c065873443014e0371f1ed656c586c6730bf927415757f389d92acf8268df")); + bytes kdfExpect2(fromHex("0x4050c52e6d9c08755e5a818ac66fabe478b825b1836fd5efc4d44e40d04dabcc")); + bytes kdfTest2; + kdfTest2 = s_secp256k1.eciesKDF(kdfInput2, bytes(), 32); + BOOST_REQUIRE(kdfTest2 == kdfExpect2); + + KeyPair k(Secret(fromHex("0x332143e9629eedff7d142d741f896258f5a1bfab54dab2121d3ec5000093d74b"))); + Public p(fromHex("0xf0d2b97981bd0d415a843b5dfe8ab77a30300daab3658c578f2340308a2da1a07f0821367332598b6aa4e180a41e92f4ebbae3518da847f0b1c0bbfe20bcf4e1")); + Secret agreeExpected(fromHex("0xee1418607c2fcfb57fda40380e885a707f49000a5dda056d828b7d9bd1f29a08")); + Secret agreeTest; + s_secp256k1.agree(k.sec(), p, agreeTest); + BOOST_REQUIRE(agreeExpected == agreeTest); + + KeyPair kmK(Secret(fromHex("0x57baf2c62005ddec64c357d96183ebc90bf9100583280e848aa31d683cad73cb"))); + bytes kmCipher(fromHex("0x04ff2c874d0a47917c84eea0b2a4141ca95233720b5c70f81a8415bae1dc7b746b61df7558811c1d6054333907333ef9bb0cc2fbf8b34abb9730d14e0140f4553f4b15d705120af46cf653a1dc5b95b312cf8444714f95a4f7a0425b67fc064d18f4d0a528761565ca02d97faffdac23de10")); + bytes kmPlain = kmCipher; + bytes kmExpected(asBytes("a")); + BOOST_REQUIRE(s_secp256k1.decryptECIES(kmK.sec(), kmPlain)); + BOOST_REQUIRE(kmExpected == kmPlain); + + KeyPair kenc(Secret(fromHex("0x472413e97f1fd58d84e28a559479e6b6902d2e8a0cee672ef38a3a35d263886b"))); + Public penc(Public(fromHex("0x7a2aa2951282279dc1171549a7112b07c38c0d97c0fe2c0ae6c4588ba15be74a04efc4f7da443f6d61f68a9279bc82b73e0cc8d090048e9f87e838ae65dd8d4c"))); + BOOST_REQUIRE(penc == kenc.pub()); + + bytes cipher1(fromHex("0x046f647e1bd8a5cd1446d31513bac233e18bdc28ec0e59d46de453137a72599533f1e97c98154343420d5f16e171e5107999a7c7f1a6e26f57bcb0d2280655d08fb148d36f1d4b28642d3bb4a136f0e33e3dd2e3cffe4b45a03fb7c5b5ea5e65617250fdc89e1a315563c20504b9d3a72555")); + bytes plainTest1 = cipher1; + bytes expectedPlain1 = asBytes("a"); + BOOST_REQUIRE(s_secp256k1.decryptECIES(kenc.sec(), plainTest1)); + BOOST_REQUIRE(plainTest1 == expectedPlain1); + + bytes cipher2(fromHex("0x0443c24d6ccef3ad095140760bb143078b3880557a06392f17c5e368502d79532bc18903d59ced4bbe858e870610ab0d5f8b7963dd5c9c4cf81128d10efd7c7aa80091563c273e996578403694673581829e25a865191bdc9954db14285b56eb0043b6288172e0d003c10f42fe413222e273d1d4340c38a2d8344d7aadcbc846ee")); + bytes plainTest2 = cipher2; + bytes expectedPlain2 = asBytes("aaaaaaaaaaaaaaaa"); + BOOST_REQUIRE(s_secp256k1.decryptECIES(kenc.sec(), plainTest2)); + BOOST_REQUIRE(plainTest2 == expectedPlain2); + + bytes cipher3(fromHex("0x04c4e40c86bb5324e017e598c6d48c19362ae527af8ab21b077284a4656c8735e62d73fb3d740acefbec30ca4c024739a1fcdff69ecaf03301eebf156eb5f17cca6f9d7a7e214a1f3f6e34d1ee0ec00ce0ef7d2b242fbfec0f276e17941f9f1bfbe26de10a15a6fac3cda039904ddd1d7e06e7b96b4878f61860e47f0b84c8ceb64f6a900ff23844f4359ae49b44154980a626d3c73226c19e")); + bytes plainTest3 = cipher3; + bytes expectedPlain3 = asBytes("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); + BOOST_REQUIRE(s_secp256k1.decryptECIES(kenc.sec(), plainTest3)); + BOOST_REQUIRE(plainTest3 == expectedPlain3); +} + +BOOST_AUTO_TEST_SUITE_END() + From d137581c7ff68ff7d7e43b106de430c001d81e6e Mon Sep 17 00:00:00 2001 From: subtly Date: Thu, 5 Mar 2015 03:07:14 +0100 Subject: [PATCH 026/100] authenticated capability (hello) and handshake authentication --- libp2p/Host.cpp | 303 +++++---------------------- libp2p/Host.h | 79 +------ libp2p/Peer.h | 3 +- libp2p/RLPxHandshake.cpp | 433 ++++++++++++++++++++++----------------- libp2p/RLPxHandshake.h | 54 +++-- libp2p/Session.cpp | 94 +-------- libp2p/Session.h | 2 +- test/rlpx.cpp | 2 +- 8 files changed, 343 insertions(+), 627 deletions(-) diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index 0062722c6..5fc6ce3b9 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -34,13 +34,12 @@ #include "Common.h" #include "Capability.h" #include "UPnP.h" +#include "RLPxHandshake.h" #include "Host.h" using namespace std; using namespace dev; using namespace dev::p2p; -#include "RLPxHandshake.h" - HostNodeTableHandler::HostNodeTableHandler(Host& _host): m_host(_host) {} void HostNodeTableHandler::processEvent(NodeId const& _n, NodeTableEventType const& _e) @@ -157,23 +156,63 @@ unsigned Host::protocolVersion() const return 3; } -void Host::registerPeer(std::shared_ptr _s, CapDescs const& _caps) +bool Host::startPeerSession(Public const& _id, RLP const& _rlp, bi::tcp::socket *_socket) { + /// Get or create Peer + shared_ptr p; + p = m_peers[_id]; + if (!p) + { + p.reset(new Peer()); // this maybe redundant + p->id = _id; + } + p->m_lastDisconnect = NoDisconnect; + if (p->isOffline()) + p->m_lastConnected = std::chrono::system_clock::now(); + p->m_failedAttempts = 0; + // TODO: update pendingconns w/session-weak-ptr for graceful shutdown (otherwise this line isn't safe) + p->endpoint.tcp.address(_socket->remote_endpoint().address()); + + auto protocolVersion = _rlp[1].toInt(); + auto clientVersion = _rlp[2].toString(); + auto caps = _rlp[3].toVector(); + auto listenPort = _rlp[4].toInt(); + + // clang error (previously: ... << hex << caps ...) + // "'operator<<' should be declared prior to the call site or in an associated namespace of one of its arguments" + stringstream capslog; + for (auto cap: caps) + capslog << "(" << cap.first << "," << dec << cap.second << ")"; + clog(NetMessageSummary) << "Hello: " << clientVersion << "V[" << protocolVersion << "]" << _id.abridged() << showbase << capslog.str() << dec << listenPort; + + // create session so disconnects are managed + auto ps = make_shared(this, move(*_socket), p, PeerSessionInfo({_id, clientVersion, _socket->remote_endpoint().address().to_string(), listenPort, chrono::steady_clock::duration(), _rlp[3].toSet(), 0, map()})); + if (protocolVersion != this->protocolVersion()) + { + ps->disconnect(IncompatibleProtocol); + return false; + } + { - clog(NetNote) << "p2p.host.peer.register" << _s->m_peer->id.abridged(); RecursiveGuard l(x_sessions); - // TODO: temporary loose-coupling; if m_peers already has peer, - // it is same as _s->m_peer. (fixing next PR) - if (!m_peers.count(_s->m_peer->id)) - m_peers[_s->m_peer->id] = _s->m_peer; - m_sessions[_s->m_peer->id] = _s; + if (m_sessions.count(_id) && !!m_sessions[_id].lock()) + { + // Already connected. + clog(NetWarn) << "Session already exists for peer with id" << _id.abridged(); + ps->disconnect(DuplicatePeer); + return false; + } + + clog(NetNote) << "p2p.host.peer.register" << _id.abridged(); + m_sessions[_id] = ps; } + ps->start(); unsigned o = (unsigned)UserPacket; - for (auto const& i: _caps) + for (auto const& i: caps) if (haveCapability(i)) { - _s->m_capabilities[i] = shared_ptr(m_capabilities[i]->newPeerCapability(_s.get(), o)); + ps->m_capabilities[i] = shared_ptr(m_capabilities[i]->newPeerCapability(ps.get(), o)); o += m_capabilities[i]->messageCount(); } } @@ -340,7 +379,7 @@ void Host::runAcceptor() { // doHandshake takes ownersihp of *s via std::move // incoming connection; we don't yet know nodeid - auto handshake = make_shared(this, s); + auto handshake = make_shared(this, s); handshake->start(); success = true; } @@ -374,244 +413,6 @@ void Host::runAcceptor() } } -void PeerHandshake::transition(boost::system::error_code _ech) -{ - if (_ech) - { - boost::system::error_code ec; - socket->shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); - if (socket->is_open()) - socket->close(); - return; - } - - auto self(shared_from_this()); - if (nextState == New) - { - nextState = AckAuth; - - clog(NetConnect) << "Authenticating connection for " << socket->remote_endpoint(); - - if (originated) - { - clog(NetConnect) << "devp2p.connect.egress sending auth"; - // egress: tx auth - asserts((bool)remote); - auth.resize(Signature::size + h256::size + Public::size + h256::size + 1); - bytesRef sig(&auth[0], Signature::size); - bytesRef hepubk(&auth[Signature::size], h256::size); - bytesRef pubk(&auth[Signature::size + h256::size], Public::size); - bytesRef nonce(&auth[Signature::size + h256::size + Public::size], h256::size); - - // E(remote-pubk, S(ecdhe-random, ecdh-shared-secret^nonce) || H(ecdhe-random-pubk) || pubk || nonce || 0x0) - crypto::ecdh::agree(host->m_alias.sec(), remote, ss); - sign(ecdhe.seckey(), ss ^ this->nonce).ref().copyTo(sig); - sha3(ecdhe.pubkey().ref(), hepubk); - host->m_alias.pub().ref().copyTo(pubk); - this->nonce.ref().copyTo(nonce); - auth[auth.size() - 1] = 0x0; - encryptECIES(remote, &auth, authCipher); - - ba::async_write(*socket, ba::buffer(authCipher), [this, self](boost::system::error_code ec, std::size_t) - { - transition(ec); - }); - } - else - { - clog(NetConnect) << "devp2p.connect.ingress recving auth"; - // ingress: rx auth - authCipher.resize(307); - ba::async_read(*socket, ba::buffer(authCipher, 307), [this, self](boost::system::error_code ec, std::size_t) - { - if (ec) - transition(ec); - else - { - if (!decryptECIES(host->m_alias.sec(), bytesConstRef(&authCipher), auth)) - { - clog(NetWarn) << "devp2p.connect.egress recving auth decrypt failed"; - nextState = Error; - transition(); - return; - } - - bytesConstRef sig(&auth[0], Signature::size); - bytesConstRef hepubk(&auth[Signature::size], h256::size); - bytesConstRef pubk(&auth[Signature::size + h256::size], Public::size); - bytesConstRef nonce(&auth[Signature::size + h256::size + Public::size], h256::size); - pubk.copyTo(remote.ref()); - nonce.copyTo(remoteNonce.ref()); - - crypto::ecdh::agree(host->m_alias.sec(), remote, ss); - remoteEphemeral = recover(*(Signature*)sig.data(), ss ^ remoteNonce); - assert(sha3(remoteEphemeral) == *(h256*)hepubk.data()); - transition(); - } - }); - } - } - else if (nextState == AckAuth) - { - nextState = Authenticating; - - if (originated) - { - clog(NetConnect) << "devp2p.connect.egress recving ack"; - // egress: rx ack - ackCipher.resize(210); - ba::async_read(*socket, ba::buffer(ackCipher, 210), [this, self](boost::system::error_code ec, std::size_t) - { - if (ec) - transition(ec); - else - { - if (!decryptECIES(host->m_alias.sec(), bytesConstRef(&ackCipher), ack)) - { - clog(NetWarn) << "devp2p.connect.egress recving ack decrypt failed"; - nextState = Error; - transition(); - return; - } - - bytesConstRef(&ack).cropped(0, Public::size).copyTo(remoteEphemeral.ref()); - bytesConstRef(&ack).cropped(Public::size, h256::size).copyTo(remoteNonce.ref()); - transition(); - } - }); - } - else - { - clog(NetConnect) << "devp2p.connect.ingress sending ack"; - // ingress: tx ack - ack.resize(Public::size + h256::size + 1); - bytesRef epubk(&ack[0], Public::size); - bytesRef nonce(&ack[Public::size], h256::size); - ecdhe.pubkey().ref().copyTo(epubk); - this->nonce.ref().copyTo(nonce); - ack[ack.size() - 1] = 0x0; - encryptECIES(remote, &ack, ackCipher); - ba::async_write(*socket, ba::buffer(ackCipher), [this, self](boost::system::error_code ec, std::size_t) - { - transition(ec); - }); - } - } - else if (nextState == Authenticating) - { - if (originated) - clog(NetConnect) << "devp2p.connect.egress sending magic sequence"; - else - clog(NetConnect) << "devp2p.connect.ingress sending magic sequence"; - - PeerSecrets* k = new PeerSecrets; - bytes keyMaterialBytes(512); - bytesRef keyMaterial(&keyMaterialBytes); - - ecdhe.agree(remoteEphemeral, ess); - ess.ref().copyTo(keyMaterial.cropped(0, h256::size)); - ss.ref().copyTo(keyMaterial.cropped(h256::size, h256::size)); - // auto token = sha3(ssA); - k->encryptK = sha3(keyMaterial); - k->encryptK.ref().copyTo(keyMaterial.cropped(h256::size, h256::size)); - k->macK = sha3(keyMaterial); - - // Initiator egress-mac: sha3(mac-secret^recipient-nonce || auth-sent-init) - // ingress-mac: sha3(mac-secret^initiator-nonce || auth-recvd-ack) - // Recipient egress-mac: sha3(mac-secret^initiator-nonce || auth-sent-ack) - // ingress-mac: sha3(mac-secret^recipient-nonce || auth-recvd-init) - - bytes const& egressCipher = originated ? authCipher : ackCipher; - keyMaterialBytes.resize(h256::size + egressCipher.size()); - keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); - (k->macK ^ remoteNonce).ref().copyTo(keyMaterial); - bytesConstRef(&egressCipher).copyTo(keyMaterial.cropped(h256::size, egressCipher.size())); - k->egressMac = sha3(keyMaterial); - - bytes const& ingressCipher = originated ? ackCipher : authCipher; - keyMaterialBytes.resize(h256::size + ingressCipher.size()); - keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); - (k->macK ^ nonce).ref().copyTo(keyMaterial); - bytesConstRef(&ingressCipher).copyTo(keyMaterial.cropped(h256::size, ingressCipher.size())); - k->ingressMac = sha3(keyMaterial); - - - - - // This test will be replaced with protocol-capabilities information (was Hello packet) - // TESTING: send encrypt magic sequence - bytes magic {0x22,0x40,0x08,0x91}; - - - // rlpx encrypt - encryptSymNoAuth(k->encryptK, &magic, k->magicCipherAndMac, h128()); - k->magicCipherAndMac.resize(k->magicCipherAndMac.size() + 32); - sha3mac(k->egressMac.ref(), &magic, k->egressMac.ref()); - k->egressMac.ref().copyTo(bytesRef(&k->magicCipherAndMac).cropped(k->magicCipherAndMac.size() - 32, 32)); - - - - clog(NetConnect) << "devp2p.connect.egress txrx magic sequence"; - k->recvdMagicCipherAndMac.resize(k->magicCipherAndMac.size()); - - ba::async_write(*socket, ba::buffer(k->magicCipherAndMac), [this, self, k, magic](boost::system::error_code ec, std::size_t) - { - if (ec) - { - delete k; - transition(ec); - return; - } - - ba::async_read(*socket, ba::buffer(k->recvdMagicCipherAndMac, k->magicCipherAndMac.size()), [this, self, k, magic](boost::system::error_code ec, std::size_t) - { - if (originated) - clog(NetNote) << "devp2p.connect.egress recving magic sequence"; - else - clog(NetNote) << "devp2p.connect.ingress recving magic sequence"; - - if (ec) - { - delete k; - transition(ec); - return; - } - - /// capabilities handshake (encrypted magic sequence is placeholder) - bytes decryptedMagic; - decryptSymNoAuth(k->encryptK, h128(), &k->recvdMagicCipherAndMac, decryptedMagic); - if (decryptedMagic[0] == 0x22 && decryptedMagic[1] == 0x40 && decryptedMagic[2] == 0x08 && decryptedMagic[3] == 0x91) - { - shared_ptr p; - p = host->m_peers[remote]; - if (!p) - { - p.reset(new Peer()); - p->id = remote; - } - p->endpoint.tcp.address(socket->remote_endpoint().address()); - p->m_lastDisconnect = NoDisconnect; - p->m_lastConnected = std::chrono::system_clock::now(); - p->m_failedAttempts = 0; - - auto ps = std::make_shared(host, move(*socket), p); - ps->start(); - } - - // todo: PeerSession will take ownership of k and use it to encrypt wireline. - delete k; - }); - }); - } - else - { - clog(NetConnect) << "Disconnecting " << socket->remote_endpoint() << " (Authentication Failed)"; - boost::system::error_code ec; - socket->shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); - socket->close(); - } -} - string Host::pocHost() { vector strs; @@ -699,7 +500,7 @@ void Host::connect(std::shared_ptr const& _p) else { clog(NetConnect) << "Connected to" << _p->id.abridged() << "@" << _p->peerEndpoint(); - auto handshake = make_shared(this, s, _p->id); + auto handshake = make_shared(this, s, _p->id); handshake->start(); } Guard l(x_pendingNodeConns); diff --git a/libp2p/Host.h b/libp2p/Host.h index c34ec9f21..1d17d4518 100644 --- a/libp2p/Host.h +++ b/libp2p/Host.h @@ -77,9 +77,7 @@ private: class Host: public Worker { friend class HostNodeTableHandler; - - friend struct PeerHandshake; - friend struct RLPXHandshake; + friend class RLPXHandshake; friend class Session; friend class HostCapabilityFace; @@ -110,8 +108,6 @@ public: CapDescs caps() const { CapDescs ret; for (auto const& i: m_capabilities) ret.push_back(i.first); return ret; } template std::shared_ptr cap() const { try { return std::static_pointer_cast(m_capabilities.at(std::make_pair(T::staticName(), T::staticVersion()))); } catch (...) { return nullptr; } } - bool havePeerSession(NodeId _id) { RecursiveGuard l(x_sessions); return m_sessions.count(_id) ? !!m_sessions[_id].lock() : false; } - void addNode(NodeId const& _node, std::string const& _addr, unsigned short _tcpPort, unsigned short _udpPort); /// Set ideal number of peers. @@ -149,7 +145,8 @@ public: NodeId id() const { return m_alias.pub(); } - void registerPeer(std::shared_ptr _s, CapDescs const& _caps); + /// Validates and starts peer session, taking ownership of _socket. Disconnects and returns false upon error. + bool startPeerSession(Public const& _id, RLP const& _hello, bi::tcp::socket *_socket); protected: void onNodeTableEvent(NodeId const& _n, NodeTableEventType const& _e); @@ -158,6 +155,8 @@ protected: void restoreNetwork(bytesConstRef _b); private: + bool havePeerSession(NodeId _id) { RecursiveGuard l(x_sessions); return m_sessions.count(_id) ? !!m_sessions[_id].lock() : false; } + /// Populate m_peerAddresses with available public addresses. void determinePublic(std::string const& _publicAddress, bool _upnp); @@ -234,73 +233,5 @@ private: bool m_accepting = false; }; -/** - * @brief Key material and derived secrets for TCP peer connection. - */ -struct PeerSecrets -{ - friend struct PeerHandshake; - friend struct RLPXHandshake; - -protected: - Secret encryptK; - Secret macK; - h256 egressMac; - h256 ingressMac; - - bytes magicCipherAndMac; - bytes recvdMagicCipherAndMac; -}; - -struct PeerHandshake: public std::enable_shared_from_this -{ - friend class Host; - enum State - { - Error = -1, - New, // New->AckAuth [egress: tx auth, ingress: rx auth] - AckAuth, // AckAuth->Authenticating [egress: rx ack, ingress: tx ack] - Authenticating, // Authenticating [tx caps, rx caps, authenticate] - }; - - /// Handshake for ingress connection. Takes ownership of socket. - PeerHandshake(Host* _host, bi::tcp::socket* _socket): host(_host), socket(std::move(_socket)), originated(false) { crypto::Nonce::get().ref().copyTo(nonce.ref()); } - - /// Handshake for egress connection to _remote. Takes ownership of socket. - PeerHandshake(Host* _host, bi::tcp::socket* _socket, NodeId _remote): host(_host), socket(std::move(_socket)), originated(true), remote(_remote) { crypto::Nonce::get().ref().copyTo(nonce.ref()); } - - ~PeerHandshake() { delete socket; } - -protected: - void start() { transition(); } - -private: - void transition(boost::system::error_code _ech = boost::system::error_code()); - - /// Current state of handshake. - State nextState = New; - - Host* host; - - /// Node id of remote host for socket. - NodeId remote; - - bi::tcp::socket* socket; - bool originated = false; - - bytes auth; - bytes authCipher; - bytes ack; - bytes ackCipher; - Secret ss; - Secret ess; - - crypto::ECDHE ecdhe; - h256 nonce; - - Public remoteEphemeral; - h256 remoteNonce; -}; - } } diff --git a/libp2p/Peer.h b/libp2p/Peer.h index e9b45845b..743d941db 100644 --- a/libp2p/Peer.h +++ b/libp2p/Peer.h @@ -55,8 +55,7 @@ class Peer: public Node friend class Session; /// Allows Session to update score and rating. friend class Host; /// For Host: saveNetwork(), restoreNetwork() - friend struct PeerHandshake; - friend struct RLPXHandshake; + friend class RLPXHandshake; public: bool isOffline() const { return !m_session.lock(); } diff --git a/libp2p/RLPxHandshake.cpp b/libp2p/RLPxHandshake.cpp index 18db43577..77bc64e1b 100644 --- a/libp2p/RLPxHandshake.cpp +++ b/libp2p/RLPxHandshake.cpp @@ -28,7 +28,7 @@ using namespace dev; using namespace dev::p2p; using namespace CryptoPP; -RLPXFrameIO::RLPXFrameIO(RLPXHandshake& _init): m_macEnc() +RLPXFrameIO::RLPXFrameIO(RLPXHandshake const& _init): m_socket(_init.socket) { // we need: // originated? @@ -36,7 +36,7 @@ RLPXFrameIO::RLPXFrameIO(RLPXHandshake& _init): m_macEnc() // authCipher // ackCipher - bytes keyMaterialBytes(512); + bytes keyMaterialBytes(64); bytesRef keyMaterial(&keyMaterialBytes); // shared-secret = sha3(ecdhe-shared-secret || sha3(nonce || initiator-nonce)) @@ -44,23 +44,24 @@ RLPXFrameIO::RLPXFrameIO(RLPXHandshake& _init): m_macEnc() _init.ecdhe.agree(_init.remoteEphemeral, ephemeralShared); ephemeralShared.ref().copyTo(keyMaterial.cropped(0, h256::size)); h512 nonceMaterial; - h256& leftNonce = _init.originated ? _init.remoteNonce : _init.nonce; - h256& rightNonce = _init.originated ? _init.nonce : _init.remoteNonce; + h256 const& leftNonce = _init.originated ? _init.remoteNonce : _init.nonce; + h256 const& rightNonce = _init.originated ? _init.nonce : _init.remoteNonce; leftNonce.ref().copyTo(nonceMaterial.ref().cropped(0, h256::size)); rightNonce.ref().copyTo(nonceMaterial.ref().cropped(h256::size, h256::size)); auto outRef(keyMaterial.cropped(h256::size, h256::size)); sha3(nonceMaterial.ref(), outRef); // output h(nonces) sha3(keyMaterial, outRef); // output shared-secret - // token: sha3(outRef) + // token: sha3(outRef, bytesRef(&token)); -> Host (to be saved to disk) // aes-secret = sha3(ecdhe-shared-secret || shared-secret) sha3(keyMaterial, outRef); // output aes-secret - m_frameEnc.SetKey(outRef.data(), h256::size); + m_frameEnc.SetKeyWithIV(outRef.data(), h128::size, h128().data()); + m_frameDec.SetKeyWithIV(outRef.data(), h128::size, h128().data()); // mac-secret = sha3(ecdhe-shared-secret || aes-secret) sha3(keyMaterial, outRef); // output mac-secret - m_macEnc.SetKey(outRef.data(), h256::size); + m_macEnc.SetKey(outRef.data(), h128::size); // Initiator egress-mac: sha3(mac-secret^recipient-nonce || auth-sent-init) // ingress-mac: sha3(mac-secret^initiator-nonce || auth-recvd-ack) @@ -75,7 +76,7 @@ RLPXFrameIO::RLPXFrameIO(RLPXHandshake& _init): m_macEnc() m_egressMac.Update(keyMaterial.data(), keyMaterial.size()); // recover mac-secret by re-xoring remoteNonce - (*(h256*)outRef.data() ^ _init.remoteNonce ^ _init.nonce).ref().copyTo(keyMaterial); + (*(h256*)keyMaterial.data() ^ _init.remoteNonce ^ _init.nonce).ref().copyTo(keyMaterial); bytes const& ingressCipher = _init.originated ? _init.ackCipher : _init.authCipher; keyMaterialBytes.resize(h256::size + ingressCipher.size()); keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); @@ -83,25 +84,60 @@ RLPXFrameIO::RLPXFrameIO(RLPXHandshake& _init): m_macEnc() m_ingressMac.Update(keyMaterial.data(), keyMaterial.size()); } -void RLPXFrameIO::writeFullPacketFrame(bytesConstRef _packet) +void RLPXFrameIO::writeSingleFramePacket(bytesConstRef _packet, bytes& o_bytes) { + // _packet = type || rlpList() + + // current/old packet format: prep(_s).appendList(_args + 1).append((unsigned)_id); RLPStream header; + header.appendRaw(bytes({byte(_packet.size() >> 16), byte(_packet.size() >> 8), byte(_packet.size())})); + // zeroHeader: []byte{0xC2, 0x80, 0x80}. Should be rlpList(protocolType,seqId,totalPacketSize). + header.appendRaw(bytes({0xc2,0x80,0x80})); -} - -void RLPXFrameIO::writeHeader(bi::tcp::socket* _socket, h128 const& _header) -{ + // TODO: SECURITY check that header is <= 16 bytes + bytes headerWithMac; + header.swapOut(headerWithMac); + headerWithMac.resize(32); + m_frameEnc.ProcessData(headerWithMac.data(), headerWithMac.data(), 16); + updateEgressMACWithHeader(bytesConstRef(&headerWithMac).cropped(0, 16)); + egressDigest().ref().copyTo(bytesRef(&headerWithMac).cropped(h128::size,h128::size)); + + auto padding = (16 - (_packet.size() % 16)) % 16; + o_bytes.swap(headerWithMac); + o_bytes.resize(32 + _packet.size() + padding + h128::size); + bytesRef packetRef(o_bytes.data() + 32, _packet.size()); + m_frameEnc.ProcessData(packetRef.data(), _packet.data(), _packet.size()); + bytesRef paddingRef(o_bytes.data() + 32 + _packet.size(), padding); + if (padding) + m_frameEnc.ProcessData(paddingRef.data(), paddingRef.data(), padding); + bytesRef packetWithPaddingRef(o_bytes.data() + 32, _packet.size() + padding); + updateEgressMACWithEndOfFrame(packetWithPaddingRef); + bytesRef macRef(o_bytes.data() + 32 + _packet.size() + padding, h128::size); + egressDigest().ref().copyTo(macRef); + clog(NetConnect) << "SENT FRAME " << _packet.size() << *(h128*)macRef.data(); + clog(NetConnect) << "FRAME TAIL " << *(h128*)(o_bytes.data() + 32 + _packet.size() + padding); } -void RLPXFrameIO::write(bi::tcp::socket* _socket, bytesConstRef _in, bool _eof) +bool RLPXFrameIO::authAndDecryptHeader(h256& io) { - + updateIngressMACWithHeader(io.ref()); + bytesConstRef macRef = io.ref().cropped(h128::size, h128::size); + if (*(h128*)macRef.data() != ingressDigest()) + return false; + m_frameDec.ProcessData(io.data(), io.data(), 16); + return true; } -bool RLPXFrameIO::read(bytesConstRef _in, bytes& o_out) +bool RLPXFrameIO::authAndDecryptFrame(bytesRef io) { - + bytesRef cipherText(io.cropped(0, io.size() - h128::size)); + updateIngressMACWithEndOfFrame(cipherText); + bytesConstRef frameMac(io.data() + io.size() - h128::size, h128::size); + if (*(h128*)frameMac.data() != ingressDigest()) + return false; + m_frameDec.ProcessData(io.data(), io.data(), io.size() - h128::size); + return true; } h128 RLPXFrameIO::egressDigest() @@ -120,9 +156,8 @@ h128 RLPXFrameIO::ingressDigest() return move(digest); } -void RLPXFrameIO::updateEgressMACWithHeader(h128 const& _headerCipher) +void RLPXFrameIO::updateEgressMACWithHeader(bytesConstRef _headerCipher) { - m_egressMac.Update(_headerCipher.data(), h128::size); updateMAC(m_egressMac, *(h128*)_headerCipher.data()); } @@ -130,11 +165,16 @@ void RLPXFrameIO::updateEgressMACWithEndOfFrame(bytesConstRef _cipher) { m_egressMac.Update(_cipher.data(), _cipher.size()); updateMAC(m_egressMac); + { + SHA3_256 prev(m_egressMac); + h128 digest; + prev.TruncatedFinal(digest.data(), h128::size); + clog(NetConnect) << "EGRESS FRAMEMAC " << _cipher.size() << digest; + } } void RLPXFrameIO::updateIngressMACWithHeader(bytesConstRef _headerCipher) { - m_ingressMac.Update(_headerCipher.data(), h128::size); updateMAC(m_ingressMac, *(h128*)_headerCipher.data()); } @@ -142,6 +182,12 @@ void RLPXFrameIO::updateIngressMACWithEndOfFrame(bytesConstRef _cipher) { m_ingressMac.Update(_cipher.data(), _cipher.size()); updateMAC(m_ingressMac); + { + SHA3_256 prev(m_ingressMac); + h128 digest; + prev.TruncatedFinal(digest.data(), h128::size); + clog(NetConnect) << "INGRESS FRAMEMAC " << _cipher.size() << digest; + } } void RLPXFrameIO::updateMAC(SHA3_256& _mac, h128 const& _seed) @@ -155,12 +201,13 @@ void RLPXFrameIO::updateMAC(SHA3_256& _mac, h128 const& _seed) encDigest ^= (!!_seed ? _seed : prevDigestOut); // update mac for final digest - _mac.Update(encDigest.data(), h256::size); + _mac.Update(encDigest.data(), h128::size); } -void RLPXHandshake::generateAuth() +void RLPXHandshake::writeAuth() { + clog(NetConnect) << "p2p.connect.egress sending auth to " << socket->remote_endpoint(); auth.resize(Signature::size + h256::size + Public::size + h256::size + 1); bytesRef sig(&auth[0], Signature::size); bytesRef hepubk(&auth[Signature::size], h256::size); @@ -168,211 +215,227 @@ void RLPXHandshake::generateAuth() bytesRef nonce(&auth[Signature::size + h256::size + Public::size], h256::size); // E(remote-pubk, S(ecdhe-random, ecdh-shared-secret^nonce) || H(ecdhe-random-pubk) || pubk || nonce || 0x0) -//crypto::ecdh::agree(host->m_alias.sec(), remote, ss); -//sign(ecdhe.seckey(), ss ^ this->nonce).ref().copyTo(sig); + Secret staticShared; + crypto::ecdh::agree(host->m_alias.sec(), remote, staticShared); + sign(ecdhe.seckey(), staticShared ^ this->nonce).ref().copyTo(sig); sha3(ecdhe.pubkey().ref(), hepubk); host->m_alias.pub().ref().copyTo(pubk); this->nonce.ref().copyTo(nonce); auth[auth.size() - 1] = 0x0; encryptECIES(remote, &auth, authCipher); + + auto self(shared_from_this()); + ba::async_write(*socket, ba::buffer(authCipher), [this, self](boost::system::error_code ec, std::size_t) + { + transition(ec); + }); } -void RLPXHandshake::generateAck() +void RLPXHandshake::writeAck() { + clog(NetConnect) << "p2p.connect.ingress sending ack to " << socket->remote_endpoint(); + ack.resize(Public::size + h256::size + 1); + bytesRef epubk(&ack[0], Public::size); + bytesRef nonce(&ack[Public::size], h256::size); + ecdhe.pubkey().ref().copyTo(epubk); + this->nonce.ref().copyTo(nonce); + ack[ack.size() - 1] = 0x0; + encryptECIES(remote, &ack, ackCipher); + auto self(shared_from_this()); + ba::async_write(*socket, ba::buffer(ackCipher), [this, self](boost::system::error_code ec, std::size_t) + { + transition(ec); + }); } -bool RLPXHandshake::decodeAuth() +void RLPXHandshake::readAuth() { - if (!decryptECIES(host->m_alias.sec(), bytesConstRef(&authCipher), auth)) - return false; - - bytesConstRef sig(&auth[0], Signature::size); - bytesConstRef hepubk(&auth[Signature::size], h256::size); - bytesConstRef pubk(&auth[Signature::size + h256::size], Public::size); - bytesConstRef nonce(&auth[Signature::size + h256::size + Public::size], h256::size); - pubk.copyTo(remote.ref()); - nonce.copyTo(remoteNonce.ref()); - -//crypto::ecdh::agree(host->m_alias.sec(), remote, ss); -//remoteEphemeral = recover(*(Signature*)sig.data(), ss ^ remoteNonce); - assert(sha3(remoteEphemeral) == *(h256*)hepubk.data()); - return true; + clog(NetConnect) << "p2p.connect.ingress recving auth from " << socket->remote_endpoint(); + authCipher.resize(307); + auto self(shared_from_this()); + ba::async_read(*socket, ba::buffer(authCipher, 307), [this, self](boost::system::error_code ec, std::size_t) + { + if (ec) + transition(ec); + else if (decryptECIES(host->m_alias.sec(), bytesConstRef(&authCipher), auth)) + { + bytesConstRef sig(&auth[0], Signature::size); + bytesConstRef hepubk(&auth[Signature::size], h256::size); + bytesConstRef pubk(&auth[Signature::size + h256::size], Public::size); + bytesConstRef nonce(&auth[Signature::size + h256::size + Public::size], h256::size); + pubk.copyTo(remote.ref()); + nonce.copyTo(remoteNonce.ref()); + + Secret sharedSecret; + crypto::ecdh::agree(host->m_alias.sec(), remote, sharedSecret); + remoteEphemeral = recover(*(Signature*)sig.data(), sharedSecret ^ remoteNonce); + assert(sha3(remoteEphemeral) == *(h256*)hepubk.data()); + transition(); + } + else + { + clog(NetWarn) << "p2p.connect.egress recving auth decrypt failed for" << socket->remote_endpoint(); + nextState = Error; + transition(); + } + }); } -bool RLPXHandshake::decodeAck() +void RLPXHandshake::readAck() { - + clog(NetConnect) << "p2p.connect.egress recving ack from " << socket->remote_endpoint(); + ackCipher.resize(210); + auto self(shared_from_this()); + ba::async_read(*socket, ba::buffer(ackCipher, 210), [this, self](boost::system::error_code ec, std::size_t) + { + if (ec) + transition(ec); + else if (decryptECIES(host->m_alias.sec(), bytesConstRef(&ackCipher), ack)) + { + bytesConstRef(&ack).cropped(0, Public::size).copyTo(remoteEphemeral.ref()); + bytesConstRef(&ack).cropped(Public::size, h256::size).copyTo(remoteNonce.ref()); + transition(); + } + else + { + clog(NetWarn) << "p2p.connect.egress recving ack decrypt failed for " << socket->remote_endpoint(); + nextState = Error; + transition(); + } + }); } -/// used for protocol handshake -bytes RLPXHandshake::frame(bytesConstRef _packet) +void RLPXHandshake::error() { - + clog(NetConnect) << "Disconnecting " << socket->remote_endpoint() << " (Handshake Failed)"; + boost::system::error_code ec; + socket->shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); + if (socket->is_open()) + socket->close(); } void RLPXHandshake::transition(boost::system::error_code _ech) { if (_ech || nextState == Error) - { - clog(NetConnect) << "Disconnecting " << socket->remote_endpoint() << " (Handshake Failed)"; - boost::system::error_code ec; - socket->shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); - if (socket->is_open()) - socket->close(); - return; - } + return error(); auto self(shared_from_this()); if (nextState == New) { nextState = AckAuth; - - clog(NetConnect) << "Authenticating connection for " << socket->remote_endpoint(); - if (originated) - { - clog(NetConnect) << "p2p.connect.egress sending auth"; - generateAuth(); - ba::async_write(*socket, ba::buffer(authCipher), [this, self](boost::system::error_code ec, std::size_t) - { - transition(ec); - }); - } + writeAuth(); else - { - clog(NetConnect) << "p2p.connect.ingress recving auth"; - authCipher.resize(321); - ba::async_read(*socket, ba::buffer(authCipher, 321), [this, self](boost::system::error_code ec, std::size_t) - { - if (ec) - transition(ec); - else if (decodeAuth()) - transition(); - else - { - clog(NetWarn) << "p2p.connect.egress recving auth decrypt failed"; - nextState = Error; - transition(); - return; - } - }); - } + readAuth(); } else if (nextState == AckAuth) { - nextState = Authenticating; - + nextState = WriteHello; if (originated) - { - clog(NetConnect) << "p2p.connect.egress recving ack"; - // egress: rx ack - ackCipher.resize(225); - ba::async_read(*socket, ba::buffer(ackCipher, 225), [this, self](boost::system::error_code ec, std::size_t) - { - if (ec) - transition(ec); - else - { - if (!decryptECIES(host->m_alias.sec(), bytesConstRef(&ackCipher), ack)) - { - clog(NetWarn) << "p2p.connect.egress recving ack decrypt failed"; - nextState = Error; - transition(); - return; - } - - bytesConstRef(&ack).cropped(0, Public::size).copyTo(remoteEphemeral.ref()); - bytesConstRef(&ack).cropped(Public::size, h256::size).copyTo(remoteNonce.ref()); - transition(); - } - }); - } + readAck(); else - { - clog(NetConnect) << "p2p.connect.ingress sending ack"; - // ingress: tx ack - ack.resize(Public::size + h256::size + 1); - bytesRef epubk(&ack[0], Public::size); - bytesRef nonce(&ack[Public::size], h256::size); - ecdhe.pubkey().ref().copyTo(epubk); - this->nonce.ref().copyTo(nonce); - ack[ack.size() - 1] = 0x0; - encryptECIES(remote, &ack, ackCipher); - ba::async_write(*socket, ba::buffer(ackCipher), [this, self](boost::system::error_code ec, std::size_t) - { - transition(ec); - }); - } + writeAck(); } - else if (nextState == Authenticating) + else if (nextState == WriteHello) { + nextState = ReadHello; + if (originated) - clog(NetConnect) << "p2p.connect.egress sending magic sequence"; + clog(NetConnect) << "p2p.connect.egress sending capabilities handshake"; else - clog(NetConnect) << "p2p.connect.ingress sending magic sequence"; + clog(NetConnect) << "p2p.connect.ingress sending capabilities handshake"; - RLPXFrameIO* io = new RLPXFrameIO(*this); + io = new RLPXFrameIO(*this); + + // old packet format + // 5 arguments, HelloPacket + RLPStream s; + s.appendList(5 + 1).append((unsigned)0) + << host->protocolVersion() + << host->m_clientVersion + << host->caps() + << host->m_tcpPublic.port() + << host->id(); + bytes packet; + s.swapOut(packet); + io->writeSingleFramePacket(&packet, handshakeOutBuffer); + ba::async_write(*socket, ba::buffer(handshakeOutBuffer), [this, self](boost::system::error_code ec, std::size_t) + { + transition(ec); + }); + } + else if (nextState == ReadHello) + { + // Authenticate and decrypt initial hello frame with initial RLPXFrameIO + // and request host to start session. + nextState = StartSession; -// // This test will be replaced with protocol-capabilities information (was Hello packet) -// // TESTING: send encrypt magic sequence -// bytes magic {0x22,0x40,0x08,0x91}; -// // rlpx encrypt -// encryptSymNoAuth(k->encryptK, &magic, k->magicCipherAndMac, h128()); -// k->magicCipherAndMac.resize(k->magicCipherAndMac.size() + 32); -// sha3mac(k->egressMac.ref(), &magic, k->egressMac.ref()); -// k->egressMac.ref().copyTo(bytesRef(&k->magicCipherAndMac).cropped(k->magicCipherAndMac.size() - 32, 32)); -// -// clog(NetConnect) << "p2p.connect.egress txrx magic sequence"; -// k->recvdMagicCipherAndMac.resize(k->magicCipherAndMac.size()); -// -// ba::async_write(*socket, ba::buffer(k->magicCipherAndMac), [this, self, k, magic](boost::system::error_code ec, std::size_t) -// { -// if (ec) -// { -// delete k; -// transition(ec); -// return; -// } -// -// ba::async_read(*socket, ba::buffer(k->recvdMagicCipherAndMac, k->magicCipherAndMac.size()), [this, self, k, magic](boost::system::error_code ec, std::size_t) -// { -// if (originated) -// clog(NetNote) << "p2p.connect.egress recving magic sequence"; -// else -// clog(NetNote) << "p2p.connect.ingress recving magic sequence"; -// -// if (ec) -// { -// delete k; -// transition(ec); -// return; -// } -// -// /// capabilities handshake (encrypted magic sequence is placeholder) -// bytes decryptedMagic; -// decryptSymNoAuth(k->encryptK, h128(), &k->recvdMagicCipherAndMac, decryptedMagic); -// if (decryptedMagic[0] == 0x22 && decryptedMagic[1] == 0x40 && decryptedMagic[2] == 0x08 && decryptedMagic[3] == 0x91) -// { - shared_ptr p; - p = host->m_peers[remote]; - if (!p) + // read frame header + handshakeInBuffer.resize(h256::size); + ba::async_read(*socket, boost::asio::buffer(handshakeInBuffer, h256::size), [this,self](boost::system::error_code ec, std::size_t length) + { + if (ec) + transition(ec); + else + { + /// authenticate and decrypt header + if (!io->authAndDecryptHeader(*(h256*)handshakeInBuffer.data())) + { + nextState = Error; + transition(); + return; + } + + clog(NetNote) << (originated ? "p2p.connect.egress" : "p2p.connect.ingress") << "recvd hello header"; + + /// check frame size + bytes& header = handshakeInBuffer; + uint32_t frameSize = (uint32_t)(header[2]) | (uint32_t)(header[1])<<8 | (uint32_t)(header[0])<<16; + if (frameSize > 1024) + { + // all future frames: 16777216 + clog(NetWarn) << (originated ? "p2p.connect.egress" : "p2p.connect.ingress") << "hello frame is too large"; + nextState = Error; + transition(); + return; + } + + /// rlp of header has protocol-type, sequence-id[, total-packet-size] + bytes headerRLP(header.size() - 3 - h128::size); + bytesConstRef(&header).cropped(3).copyTo(&headerRLP); + + /// read padded frame and mac + handshakeInBuffer.resize(frameSize + ((16 - (frameSize % 16)) % 16) + h128::size); + ba::async_read(*socket, boost::asio::buffer(handshakeInBuffer, handshakeInBuffer.size()), [this, self, headerRLP](boost::system::error_code ec, std::size_t length) + { + if (ec) + transition(ec); + else { - p.reset(new Peer()); - p->id = remote; + if (!io->authAndDecryptFrame(bytesRef(&handshakeInBuffer))) + { + clog(NetWarn) << (originated ? "p2p.connect.egress" : "p2p.connect.ingress") << "hello frame: decrypt failed"; + nextState = Error; + transition(); + return; + } + + RLP rlp(handshakeInBuffer); + auto packetType = (PacketType)rlp[0].toInt(); + if (packetType != 0) + { + clog(NetWarn) << (originated ? "p2p.connect.egress" : "p2p.connect.ingress") << "hello frame: invalid packet type"; + nextState = Error; + transition(); + return; + } + + // todo: handover RLPFrameIO + host->startPeerSession(remote, rlp, socket); } - p->endpoint.tcp.address(socket->remote_endpoint().address()); - p->m_lastDisconnect = NoDisconnect; - p->m_lastConnected = std::chrono::system_clock::now(); - p->m_failedAttempts = 0; - - auto ps = std::make_shared(host, move(*socket), p); - ps->start(); -// } - - // todo: PeerSession will take ownership of k and use it to encrypt wireline. - delete io; -// }); -// }); + }); + } + }); } } diff --git a/libp2p/RLPxHandshake.h b/libp2p/RLPxHandshake.h index a0b739660..2f8cc9094 100644 --- a/libp2p/RLPxHandshake.h +++ b/libp2p/RLPxHandshake.h @@ -40,21 +40,21 @@ class RLPXHandshake; class RLPXFrameIO { public: - RLPXFrameIO(RLPXHandshake& _init); + RLPXFrameIO(RLPXHandshake const& _init); - void writeFullPacketFrame(bytesConstRef _packet); - - void writeHeader(bi::tcp::socket* _socket, h128 const& _header); - - void write(bi::tcp::socket* _socket, bytesConstRef _in, bool _eof = false); + void writeSingleFramePacket(bytesConstRef _packet, bytes& o_bytes); + + /// Authenticates and decrypts header in-place. + bool authAndDecryptHeader(h256& io_cipherWithMac); - bool read(bytesConstRef _in, bytes& o_out); + /// Authenticates and decrypts frame in-place. + bool authAndDecryptFrame(bytesRef io_cipherWithMac); h128 egressDigest(); h128 ingressDigest(); - void updateEgressMACWithHeader(h128 const& _headerCipher); + void updateEgressMACWithHeader(bytesConstRef _headerCipher); void updateEgressMACWithEndOfFrame(bytesConstRef _cipher); @@ -66,21 +66,28 @@ private: void updateMAC(CryptoPP::SHA3_256& _mac, h128 const& _seed = h128()); CryptoPP::CTR_Mode::Encryption m_frameEnc; + CryptoPP::CTR_Mode::Encryption m_frameDec; CryptoPP::ECB_Mode::Encryption m_macEnc; CryptoPP::SHA3_256 m_egressMac; CryptoPP::SHA3_256 m_ingressMac; + + bi::tcp::socket* m_socket; }; -struct RLPXHandshake: public std::enable_shared_from_this +// TODO: change properties to m_ +class RLPXHandshake: public std::enable_shared_from_this { +public: friend class RLPXFrameIO; - friend class Host; + enum State { Error = -1, New, // New->AckAuth [egress: tx auth, ingress: rx auth] - AckAuth, // AckAuth->Authenticating [egress: rx ack, ingress: tx ack] - Authenticating, // Authenticating [tx caps, rx caps, authenticate] + AckAuth, // AckAuth->WriteHello [egress: rx ack, ingress: tx ack] + WriteHello, // WriteHello [tx caps, rx caps, writehello] + ReadHello, + StartSession }; /// Handshake for ingress connection. Takes ownership of socket. @@ -91,20 +98,18 @@ struct RLPXHandshake: public std::enable_shared_from_this ~RLPXHandshake() { delete socket; } -protected: void start() { transition(); } - void generateAuth(); - bool decodeAuth(); +protected: + void writeAuth(); + void readAuth(); - void generateAck(); - bool decodeAck(); + void writeAck(); + void readAck(); - bytes frame(bytesConstRef _packet); - -private: + void error(); void transition(boost::system::error_code _ech = boost::system::error_code()); - + /// Current state of handshake. State nextState = New; @@ -121,12 +126,17 @@ private: bytes authCipher; bytes ack; bytes ackCipher; - + bytes handshakeOutBuffer; + bytes handshakeInBuffer; + crypto::ECDHE ecdhe; h256 nonce; Public remoteEphemeral; h256 remoteNonce; + + /// Frame IO is used to read frame for last step of handshake authentication. + std::unique_ptr io; }; } diff --git a/libp2p/Session.cpp b/libp2p/Session.cpp index 6ff765cf6..a9d50f6e7 100644 --- a/libp2p/Session.cpp +++ b/libp2p/Session.cpp @@ -36,11 +36,11 @@ using namespace dev::p2p; #endif #define clogS(X) dev::LogOutputStream(false) << "| " << std::setw(2) << m_socket.native_handle() << "] " -Session::Session(Host* _s, bi::tcp::socket _socket, std::shared_ptr const& _n): +Session::Session(Host* _s, bi::tcp::socket _socket, std::shared_ptr const& _n, PeerSessionInfo _info): m_server(_s), - m_socket(std::move(_socket)), + m_socket(move(_socket)), m_peer(_n), - m_info({NodeId(), "?", m_socket.remote_endpoint().address().to_string(), 0, chrono::steady_clock::duration(0), CapDescSet(), 0, map()}), + m_info(_info), m_ping(chrono::steady_clock::time_point::max()) { m_lastReceived = m_connect = chrono::steady_clock::now(); @@ -151,86 +151,6 @@ bool Session::interpret(RLP const& _r) switch ((PacketType)_r[0].toInt()) { - case HelloPacket: - { - // TODO: P2P first pass, implement signatures. if signature fails, drop connection. if egress, flag node's endpoint as stale. - // Move auth to Host so we consolidate authentication logic and eschew peer deduplication logic. - // Move all node-lifecycle information into Host. - // Finalize peer-lifecycle properties vs node lifecycle. - - m_protocolVersion = _r[1].toInt(); - auto clientVersion = _r[2].toString(); - auto caps = _r[3].toVector(); - auto listenPort = _r[4].toInt(); - auto id = _r[5].toHash(); - - // clang error (previously: ... << hex << caps ...) - // "'operator<<' should be declared prior to the call site or in an associated namespace of one of its arguments" - stringstream capslog; - for (auto cap: caps) - capslog << "(" << cap.first << "," << dec << cap.second << ")"; - - clogS(NetMessageSummary) << "Hello: " << clientVersion << "V[" << m_protocolVersion << "]" << id.abridged() << showbase << capslog.str() << dec << listenPort; - - if (m_server->id() == id) - { - // Already connected. - clogS(NetWarn) << "Connected to ourself under a false pretext. We were told this peer was id" << id.abridged(); - disconnect(LocalIdentity); - return true; - } - - // if peer and connection have id, check for UnexpectedIdentity - if (!id) - { - disconnect(NullIdentity); - return true; - } - else if (!m_peer->id) - { - m_peer->id = id; - m_peer->endpoint.tcp.port(listenPort); - } - else if (m_peer->id != id) - { - // TODO p2p: FIXME. Host should catch this and reattempt adding node to table. - m_peer->id = id; - m_peer->m_score = 0; - m_peer->m_rating = 0; -// disconnect(UnexpectedIdentity); -// return true; - } - - if (m_server->havePeerSession(id)) - { - // Already connected. - clogS(NetWarn) << "Already connected to a peer with id" << id.abridged(); - // Possible that two nodes continually connect to each other with exact same timing. - this_thread::sleep_for(chrono::milliseconds(rand() % 100)); - disconnect(DuplicatePeer); - return true; - } - - if (m_peer->isOffline()) - m_peer->m_lastConnected = chrono::system_clock::now(); - - if (m_protocolVersion != m_server->protocolVersion()) - { - disconnect(IncompatibleProtocol); - return true; - } - - m_info.clientVersion = clientVersion; - m_info.host = m_socket.remote_endpoint().address().to_string(); - m_info.port = listenPort; - m_info.lastPing = std::chrono::steady_clock::duration(); - m_info.caps = _r[3].toSet(); - m_info.socket = (unsigned)m_socket.native_handle(); - m_info.notes = map(); - - m_server->registerPeer(shared_from_this(), caps); - break; - } case DisconnectPacket: { string reason = "Unspecified"; @@ -478,14 +398,6 @@ void Session::disconnect(DisconnectReason _reason) void Session::start() { - RLPStream s; - prep(s, HelloPacket, 5) - << m_server->protocolVersion() - << m_server->m_clientVersion - << m_server->caps() - << m_server->m_tcpPublic.port() - << m_server->id(); - sealAndSend(s); ping(); doRead(); } diff --git a/libp2p/Session.h b/libp2p/Session.h index 4d55e7d9d..bfb7a2d60 100644 --- a/libp2p/Session.h +++ b/libp2p/Session.h @@ -51,7 +51,7 @@ class Session: public std::enable_shared_from_this friend class HostCapabilityFace; public: - Session(Host* _server, bi::tcp::socket _socket, std::shared_ptr const& _n); + Session(Host* _server, bi::tcp::socket _socket, std::shared_ptr const& _n, PeerSessionInfo _info); virtual ~Session(); void start(); diff --git a/test/rlpx.cpp b/test/rlpx.cpp index d845911f4..1cdde4320 100644 --- a/test/rlpx.cpp +++ b/test/rlpx.cpp @@ -141,7 +141,7 @@ BOOST_AUTO_TEST_CASE(test_secrets_cpp_vectors) bytes macSecret(32); outRef.copyTo(&macSecret); BOOST_REQUIRE(macSecret == fromHex("2ec149072353d54437422837c886b0538a9206e6c559f6b4a55f65a866867723")); - m_macEnc.SetKey(outRef.data(), h256::size); + m_macEnc.SetKey(outRef.data(), h128::size); // Initiator egress-mac: sha3(mac-secret^recipient-nonce || auth-sent-init) // ingress-mac: sha3(mac-secret^initiator-nonce || auth-recvd-ack) From 7386ad26067cd53af8bf9384e38949ea3ed0f3fb Mon Sep 17 00:00:00 2001 From: subtly Date: Thu, 5 Mar 2015 21:47:30 +0100 Subject: [PATCH 027/100] prep for framing. remove unused raw network send() methods from capabilities. bump protocol version. back out magic sequence packet prefix. --- libp2p/Capability.cpp | 10 ------- libp2p/Capability.h | 3 -- libp2p/Host.cpp | 16 ++++------ libp2p/Session.cpp | 69 ++++++++++++++++--------------------------- libp2p/Session.h | 5 ++-- 5 files changed, 35 insertions(+), 68 deletions(-) diff --git a/libp2p/Capability.cpp b/libp2p/Capability.cpp index 5ef70bbc3..60f4fd7f3 100644 --- a/libp2p/Capability.cpp +++ b/libp2p/Capability.cpp @@ -53,16 +53,6 @@ void Capability::sealAndSend(RLPStream& _s) m_session->sealAndSend(_s); } -void Capability::send(bytesConstRef _msg) -{ - m_session->send(_msg); -} - -void Capability::send(bytes&& _msg) -{ - m_session->send(move(_msg)); -} - void Capability::addRating(unsigned _r) { m_session->addRating(_r); diff --git a/libp2p/Capability.h b/libp2p/Capability.h index 04b116aa8..ad8127bb5 100644 --- a/libp2p/Capability.h +++ b/libp2p/Capability.h @@ -52,9 +52,6 @@ protected: RLPStream& prep(RLPStream& _s, unsigned _id, unsigned _args = 0); void sealAndSend(RLPStream& _s); - void send(bytes&& _msg); - void send(bytesConstRef _msg); - void addRating(unsigned _r); private: diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index 35c309a53..73e588463 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -154,7 +154,7 @@ void Host::doneWorking() unsigned Host::protocolVersion() const { - return 3; + return 4; } bool Host::startPeerSession(Public const& _id, RLP const& _rlp, bi::tcp::socket *_socket) @@ -273,15 +273,11 @@ void Host::onNodeTableEvent(NodeId const& _n, NodeTableEventType const& _e) void Host::seal(bytes& _b) { - _b[0] = 0x22; - _b[1] = 0x40; - _b[2] = 0x08; - _b[3] = 0x91; - uint32_t len = (uint32_t)_b.size() - 8; - _b[4] = (len >> 24) & 0xff; - _b[5] = (len >> 16) & 0xff; - _b[6] = (len >> 8) & 0xff; - _b[7] = len & 0xff; + uint32_t len = (uint32_t)_b.size() - 4; + _b[0] = (len >> 24) & 0xff; + _b[1] = (len >> 16) & 0xff; + _b[2] = (len >> 8) & 0xff; + _b[3] = len & 0xff; } void Host::determinePublic(string const& _publicAddress, bool _upnp) diff --git a/libp2p/Session.cpp b/libp2p/Session.cpp index 721bf1623..310ecea61 100644 --- a/libp2p/Session.cpp +++ b/libp2p/Session.cpp @@ -281,7 +281,7 @@ RLPStream& Session::prep(RLPStream& _s, PacketType _id, unsigned _args) RLPStream& Session::prep(RLPStream& _s) { - return _s.appendRaw(bytes(8, 0)); + return _s.appendRaw(bytes(4, 0)); } void Session::sealAndSend(RLPStream& _s) @@ -294,14 +294,12 @@ void Session::sealAndSend(RLPStream& _s) bool Session::checkPacket(bytesConstRef _msg) { - if (_msg.size() < 8) + if (_msg.size() < 5) return false; - if (!(_msg[0] == 0x22 && _msg[1] == 0x40 && _msg[2] == 0x08 && _msg[3] == 0x91)) + uint32_t len = ((_msg[0] * 256 + _msg[1]) * 256 + _msg[2]) * 256 + _msg[3]; + if (_msg.size() != len + 4) return false; - uint32_t len = ((_msg[4] * 256 + _msg[5]) * 256 + _msg[6]) * 256 + _msg[7]; - if (_msg.size() != len + 8) - return false; - RLP r(_msg.cropped(8)); + RLP r(_msg.cropped(4)); if (r.actualSize() != len) return false; return true; @@ -314,7 +312,7 @@ void Session::send(bytesConstRef _msg) void Session::send(bytes&& _msg) { - clogS(NetLeft) << RLP(bytesConstRef(&_msg).cropped(8)); + clogS(NetLeft) << RLP(bytesConstRef(&_msg).cropped(4)); if (!checkPacket(bytesConstRef(&_msg))) clogS(NetWarn) << "INVALID PACKET CONSTRUCTED!"; @@ -410,68 +408,53 @@ void Session::start() void Session::doRead() { - // ignore packets received while waiting to disconnect + // ignore packets received while waiting to disconnect. if (m_dropped) return; auto self(shared_from_this()); m_socket.async_read_some(boost::asio::buffer(m_data), [this,self](boost::system::error_code ec, std::size_t length) { - // If error is end of file, ignore if (ec && ec.category() != boost::asio::error::get_misc_category() && ec.value() != boost::asio::error::eof) { - // got here with length of 1241... clogS(NetWarn) << "Error reading: " << ec.message(); drop(TCPError); } else if (ec && length == 0) - { return; - } else { try { m_incoming.resize(m_incoming.size() + length); memcpy(m_incoming.data() + m_incoming.size() - length, m_data.data(), length); - while (m_incoming.size() > 8) + + // 4 bytes for length header + const uint32_t c_hLen = 4; + while (m_incoming.size() > c_hLen) { - if (m_incoming[0] != 0x22 || m_incoming[1] != 0x40 || m_incoming[2] != 0x08 || m_incoming[3] != 0x91) + // break if data recvd is less than expected size of packet. + uint32_t len = fromBigEndian(bytesConstRef(m_incoming.data(), c_hLen)); + uint32_t tlen = c_hLen + len; + if (m_incoming.size() < tlen) + break; + bytesConstRef frame(m_incoming.data(), tlen); + bytesConstRef packet = frame.cropped(c_hLen); + if (!checkPacket(frame)) { - clogS(NetWarn) << "INVALID SYNCHRONISATION TOKEN; expected = 22400891; received = " << toHex(bytesConstRef(m_incoming.data(), 4)); + cerr << "Received " << packet.size() << ": " << toHex(packet) << endl; + clogS(NetWarn) << "INVALID MESSAGE RECEIVED"; disconnect(BadProtocol); return; } else { - uint32_t len = fromBigEndian(bytesConstRef(m_incoming.data() + 4, 4)); - uint32_t tlen = len + 8; - if (m_incoming.size() < tlen) - break; - - // enough has come in. - auto data = bytesConstRef(m_incoming.data(), tlen); - if (!checkPacket(data)) - { - cerr << "Received " << len << ": " << toHex(bytesConstRef(m_incoming.data() + 8, len)) << endl; - clogS(NetWarn) << "INVALID MESSAGE RECEIVED"; - disconnect(BadProtocol); - return; - } - else - { - RLP r(data.cropped(8)); - if (!interpret(r)) - { - // error - bad protocol - clogS(NetWarn) << "Couldn't interpret packet." << RLP(r); - // Just wasting our bandwidth - perhaps reduce rating? - //return; - } - } - memmove(m_incoming.data(), m_incoming.data() + tlen, m_incoming.size() - tlen); - m_incoming.resize(m_incoming.size() - tlen); + RLP r(packet); + if (!interpret(r)) + clogS(NetWarn) << "Couldn't interpret packet." << RLP(r); } + memmove(m_incoming.data(), m_incoming.data() + tlen, m_incoming.size() - tlen); + m_incoming.resize(m_incoming.size() - tlen); } doRead(); } diff --git a/libp2p/Session.h b/libp2p/Session.h index 578e5e80a..9f87d386d 100644 --- a/libp2p/Session.h +++ b/libp2p/Session.h @@ -71,8 +71,6 @@ public: static RLPStream& prep(RLPStream& _s, PacketType _t, unsigned _args = 0); static RLPStream& prep(RLPStream& _s); void sealAndSend(RLPStream& _s); - void send(bytes&& _msg); - void send(bytesConstRef _msg); int rating() const; void addRating(unsigned _r); @@ -85,6 +83,9 @@ public: void serviceNodesRequest(); private: + void send(bytes&& _msg); + void send(bytesConstRef _msg); + /// Drop the connection for the reason @a _r. void drop(DisconnectReason _r); From 4b198939cdf6cbeb96f708413809ad45a9eb6b0f Mon Sep 17 00:00:00 2001 From: subtly Date: Thu, 5 Mar 2015 23:27:31 +0100 Subject: [PATCH 028/100] test header macs --- test/rlpx.cpp | 45 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 42 insertions(+), 3 deletions(-) diff --git a/test/rlpx.cpp b/test/rlpx.cpp index 1cdde4320..593613106 100644 --- a/test/rlpx.cpp +++ b/test/rlpx.cpp @@ -323,9 +323,48 @@ BOOST_AUTO_TEST_CASE(test_secrets_from_go) BOOST_CHECK(digest == fromHex("0x75823d96e23136c89666ee025fb21a432be906512b3dd4a3049e898adb433847")); } -// bytes initHello(fromHex("6ef23fcf1cec7312df623f9ae701e63b550cdb8517fefd8dd398fc2acd1d935e6e0434a2b96769078477637347b7b01924fff9ff1c06df2f804df3b0402bbb9f87365b3c6856b45e1e2b6470986813c3816a71bff9d69dd297a5dbd935ab578f6e5d7e93e4506a44f307c332d95e8a4b102585fd8ef9fc9e3e055537a5cec2e9")); -// -// bytes recvHello(fromHex("6ef23fcf1cec7312df623f9ae701e63be36a1cdd1b19179146019984f3625d4a6e0434a2b96769050577657247b7b02bc6c314470eca7e3ef650b98c83e9d7dd4830b3f718ff562349aead2530a8d28a8484604f92e5fced2c6183f304344ab0e7c301a0c05559f4c25db65e36820b4b909a226171a60ac6cb7beea09376d6d8")); + bytes initHello(fromHex("6ef23fcf1cec7312df623f9ae701e63b550cdb8517fefd8dd398fc2acd1d935e6e0434a2b96769078477637347b7b01924fff9ff1c06df2f804df3b0402bbb9f87365b3c6856b45e1e2b6470986813c3816a71bff9d69dd297a5dbd935ab578f6e5d7e93e4506a44f307c332d95e8a4b102585fd8ef9fc9e3e055537a5cec2e9")); + + bytes recvHello(fromHex("6ef23fcf1cec7312df623f9ae701e63be36a1cdd1b19179146019984f3625d4a6e0434a2b96769050577657247b7b02bc6c314470eca7e3ef650b98c83e9d7dd4830b3f718ff562349aead2530a8d28a8484604f92e5fced2c6183f304344ab0e7c301a0c05559f4c25db65e36820b4b909a226171a60ac6cb7beea09376d6d8")); + + /// test macs of frame headers + { + SHA3_256 egressmac(m_egressMac); + SHA3_256 prevDigest(egressmac); + h128 prevDigestOut; + prevDigest.TruncatedFinal(prevDigestOut.data(), h128::size); + h128 encDigest; + m_macEnc.ProcessData(encDigest.data(), prevDigestOut.data(), h128::size); + encDigest ^= *(h128*)initHello.data(); + egressmac.Update(encDigest.data(), h128::size); + egressmac.TruncatedFinal(encDigest.data(), h128::size); + + bytes provided(16); + bytesConstRef(&initHello).cropped(16, 16).copyTo(bytesRef(&provided)); + BOOST_REQUIRE(*(h128*)encDigest.data() == *(h128*)provided.data()); + } + + { + SHA3_256 ingressmac(m_ingressMac); + SHA3_256 prevDigest(ingressmac); + h128 prevDigestOut; + prevDigest.TruncatedFinal(prevDigestOut.data(), h128::size); + h128 encDigest; + m_macEnc.ProcessData(encDigest.data(), prevDigestOut.data(), h128::size); + encDigest ^= *(h128*)recvHello.data(); + ingressmac.Update(encDigest.data(), h128::size); + ingressmac.TruncatedFinal(encDigest.data(), h128::size); + + bytes provided(16); + bytesConstRef(&recvHello).cropped(16, 16).copyTo(bytesRef(&provided)); + BOOST_REQUIRE(*(h128*)encDigest.data() == *(h128*)provided.data()); + } + + // test decrypt of frame headers for recvHello + bytes plaintext(16); + m_frameDec.ProcessData(plaintext.data(), recvHello.data(), h128::size); + cout << "decrypt recv got: " << *(h128*)plaintext.data(); + } BOOST_AUTO_TEST_CASE(ecies_interop_test_primitives) From 23a64667e30d7d0e700888f442885677e0e8afe5 Mon Sep 17 00:00:00 2001 From: subtly Date: Fri, 6 Mar 2015 00:12:18 +0100 Subject: [PATCH 029/100] classify handshake and begin pulling rlpx into session --- libp2p/Host.cpp | 4 +- libp2p/Host.h | 6 +- libp2p/RLPxHandshake.cpp | 195 +++++++++++++++++++-------------------- libp2p/RLPxHandshake.h | 41 ++++---- libp2p/Session.cpp | 6 +- libp2p/Session.h | 6 +- 6 files changed, 131 insertions(+), 127 deletions(-) diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index 73e588463..ef2f95180 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -157,7 +157,7 @@ unsigned Host::protocolVersion() const return 4; } -bool Host::startPeerSession(Public const& _id, RLP const& _rlp, bi::tcp::socket *_socket) +bool Host::startPeerSession(Public const& _id, RLP const& _rlp, bi::tcp::socket *_socket, RLPXFrameIO* _io) { /// Get or create Peer shared_ptr p; @@ -187,7 +187,7 @@ bool Host::startPeerSession(Public const& _id, RLP const& _rlp, bi::tcp::socket clog(NetMessageSummary) << "Hello: " << clientVersion << "V[" << protocolVersion << "]" << _id.abridged() << showbase << capslog.str() << dec << listenPort; // create session so disconnects are managed - auto ps = make_shared(this, move(*_socket), p, PeerSessionInfo({_id, clientVersion, _socket->remote_endpoint().address().to_string(), listenPort, chrono::steady_clock::duration(), _rlp[3].toSet(), 0, map()})); + auto ps = make_shared(this, move(*_io), p, PeerSessionInfo({_id, clientVersion, _socket->remote_endpoint().address().to_string(), listenPort, chrono::steady_clock::duration(), _rlp[3].toSet(), 0, map()})); if (protocolVersion != this->protocolVersion()) { ps->disconnect(IncompatibleProtocol); diff --git a/libp2p/Host.h b/libp2p/Host.h index ce11eb447..a5091ae7e 100644 --- a/libp2p/Host.h +++ b/libp2p/Host.h @@ -40,6 +40,7 @@ #include "HostCapability.h" #include "Network.h" #include "Peer.h" +#include "RLPxHandshake.h" #include "Common.h" namespace ba = boost::asio; namespace bi = ba::ip; @@ -69,8 +70,7 @@ private: * @brief The Host class * Capabilities should be registered prior to startNetwork, since m_capabilities is not thread-safe. * - * @todo handshake: gracefully disconnect peer if peer already connected - * @todo abstract socket -> IPConnection + * @todo cleanup startPeerSession * @todo determinePublic: ipv6, udp * @todo handle conflict if addNode/requireNode called and Node already exists w/conflicting tcp or udp port * @todo per-session keepalive/ping instead of broadcast; set ping-timeout via median-latency @@ -151,7 +151,7 @@ public: NodeId id() const { return m_alias.pub(); } /// Validates and starts peer session, taking ownership of _socket. Disconnects and returns false upon error. - bool startPeerSession(Public const& _id, RLP const& _hello, bi::tcp::socket *_socket); + bool startPeerSession(Public const& _id, RLP const& _hello, bi::tcp::socket* _socket, RLPXFrameIO* _io); protected: void onNodeTableEvent(NodeId const& _n, NodeTableEventType const& _e); diff --git a/libp2p/RLPxHandshake.cpp b/libp2p/RLPxHandshake.cpp index bfaca0dab..8bec9b6dd 100644 --- a/libp2p/RLPxHandshake.cpp +++ b/libp2p/RLPxHandshake.cpp @@ -28,7 +28,7 @@ using namespace dev; using namespace dev::p2p; using namespace CryptoPP; -RLPXFrameIO::RLPXFrameIO(RLPXHandshake const& _init): m_socket(_init.socket) +RLPXFrameIO::RLPXFrameIO(RLPXHandshake const& _init): m_socket(_init.m_socket) { // we need: // originated? @@ -41,18 +41,18 @@ RLPXFrameIO::RLPXFrameIO(RLPXHandshake const& _init): m_socket(_init.socket) // shared-secret = sha3(ecdhe-shared-secret || sha3(nonce || initiator-nonce)) Secret ephemeralShared; - _init.ecdhe.agree(_init.remoteEphemeral, ephemeralShared); + _init.m_ecdhe.agree(_init.m_remoteEphemeral, ephemeralShared); ephemeralShared.ref().copyTo(keyMaterial.cropped(0, h256::size)); h512 nonceMaterial; - h256 const& leftNonce = _init.originated ? _init.remoteNonce : _init.nonce; - h256 const& rightNonce = _init.originated ? _init.nonce : _init.remoteNonce; + h256 const& leftNonce = _init.m_originated ? _init.m_remoteNonce : _init.m_nonce; + h256 const& rightNonce = _init.m_originated ? _init.m_nonce : _init.m_remoteNonce; leftNonce.ref().copyTo(nonceMaterial.ref().cropped(0, h256::size)); rightNonce.ref().copyTo(nonceMaterial.ref().cropped(h256::size, h256::size)); auto outRef(keyMaterial.cropped(h256::size, h256::size)); sha3(nonceMaterial.ref(), outRef); // output h(nonces) sha3(keyMaterial, outRef); // output shared-secret - // token: sha3(outRef, bytesRef(&token)); -> Host (to be saved to disk) + // token: sha3(outRef, bytesRef(&token)); -> m_host (to be saved to disk) // aes-secret = sha3(ecdhe-shared-secret || shared-secret) sha3(keyMaterial, outRef); // output aes-secret @@ -68,16 +68,16 @@ RLPXFrameIO::RLPXFrameIO(RLPXHandshake const& _init): m_socket(_init.socket) // Recipient egress-mac: sha3(mac-secret^initiator-nonce || auth-sent-ack) // ingress-mac: sha3(mac-secret^recipient-nonce || auth-recvd-init) - (*(h256*)outRef.data() ^ _init.remoteNonce).ref().copyTo(keyMaterial); - bytes const& egressCipher = _init.originated ? _init.authCipher : _init.ackCipher; + (*(h256*)outRef.data() ^ _init.m_remoteNonce).ref().copyTo(keyMaterial); + bytes const& egressCipher = _init.m_originated ? _init.m_authCipher : _init.m_ackCipher; keyMaterialBytes.resize(h256::size + egressCipher.size()); keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); bytesConstRef(&egressCipher).copyTo(keyMaterial.cropped(h256::size, egressCipher.size())); m_egressMac.Update(keyMaterial.data(), keyMaterial.size()); // recover mac-secret by re-xoring remoteNonce - (*(h256*)keyMaterial.data() ^ _init.remoteNonce ^ _init.nonce).ref().copyTo(keyMaterial); - bytes const& ingressCipher = _init.originated ? _init.ackCipher : _init.authCipher; + (*(h256*)keyMaterial.data() ^ _init.m_remoteNonce ^ _init.m_nonce).ref().copyTo(keyMaterial); + bytes const& ingressCipher = _init.m_originated ? _init.m_ackCipher : _init.m_authCipher; keyMaterialBytes.resize(h256::size + ingressCipher.size()); keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); bytesConstRef(&ingressCipher).copyTo(keyMaterial.cropped(h256::size, ingressCipher.size())); @@ -204,28 +204,27 @@ void RLPXFrameIO::updateMAC(SHA3_256& _mac, h128 const& _seed) _mac.Update(encDigest.data(), h128::size); } - void RLPXHandshake::writeAuth() { - clog(NetConnect) << "p2p.connect.egress sending auth to " << socket->remote_endpoint(); - auth.resize(Signature::size + h256::size + Public::size + h256::size + 1); - bytesRef sig(&auth[0], Signature::size); - bytesRef hepubk(&auth[Signature::size], h256::size); - bytesRef pubk(&auth[Signature::size + h256::size], Public::size); - bytesRef nonce(&auth[Signature::size + h256::size + Public::size], h256::size); + clog(NetConnect) << "p2p.connect.egress sending auth to " << m_socket->remote_endpoint(); + m_auth.resize(Signature::size + h256::size + Public::size + h256::size + 1); + bytesRef sig(&m_auth[0], Signature::size); + bytesRef hepubk(&m_auth[Signature::size], h256::size); + bytesRef pubk(&m_auth[Signature::size + h256::size], Public::size); + bytesRef nonce(&m_auth[Signature::size + h256::size + Public::size], h256::size); // E(remote-pubk, S(ecdhe-random, ecdh-shared-secret^nonce) || H(ecdhe-random-pubk) || pubk || nonce || 0x0) Secret staticShared; - crypto::ecdh::agree(host->m_alias.sec(), remote, staticShared); - sign(ecdhe.seckey(), staticShared ^ this->nonce).ref().copyTo(sig); - sha3(ecdhe.pubkey().ref(), hepubk); - host->m_alias.pub().ref().copyTo(pubk); - this->nonce.ref().copyTo(nonce); - auth[auth.size() - 1] = 0x0; - encryptECIES(remote, &auth, authCipher); + crypto::ecdh::agree(m_host->m_alias.sec(), m_remote, staticShared); + sign(m_ecdhe.seckey(), staticShared ^ m_nonce).ref().copyTo(sig); + sha3(m_ecdhe.pubkey().ref(), hepubk); + m_host->m_alias.pub().ref().copyTo(pubk); + m_nonce.ref().copyTo(nonce); + m_auth[m_auth.size() - 1] = 0x0; + encryptECIES(m_remote, &m_auth, m_authCipher); auto self(shared_from_this()); - ba::async_write(*socket, ba::buffer(authCipher), [this, self](boost::system::error_code ec, std::size_t) + ba::async_write(*m_socket, ba::buffer(m_authCipher), [this, self](boost::system::error_code ec, std::size_t) { transition(ec); }); @@ -233,17 +232,17 @@ void RLPXHandshake::writeAuth() void RLPXHandshake::writeAck() { - clog(NetConnect) << "p2p.connect.ingress sending ack to " << socket->remote_endpoint(); - ack.resize(Public::size + h256::size + 1); - bytesRef epubk(&ack[0], Public::size); - bytesRef nonce(&ack[Public::size], h256::size); - ecdhe.pubkey().ref().copyTo(epubk); - this->nonce.ref().copyTo(nonce); - ack[ack.size() - 1] = 0x0; - encryptECIES(remote, &ack, ackCipher); + clog(NetConnect) << "p2p.connect.ingress sending ack to " << m_socket->remote_endpoint(); + m_ack.resize(Public::size + h256::size + 1); + bytesRef epubk(&m_ack[0], Public::size); + bytesRef nonce(&m_ack[Public::size], h256::size); + m_ecdhe.pubkey().ref().copyTo(epubk); + m_nonce.ref().copyTo(nonce); + m_ack[m_ack.size() - 1] = 0x0; + encryptECIES(m_remote, &m_ack, m_ackCipher); auto self(shared_from_this()); - ba::async_write(*socket, ba::buffer(ackCipher), [this, self](boost::system::error_code ec, std::size_t) + ba::async_write(*m_socket, ba::buffer(m_ackCipher), [this, self](boost::system::error_code ec, std::size_t) { transition(ec); }); @@ -251,32 +250,32 @@ void RLPXHandshake::writeAck() void RLPXHandshake::readAuth() { - clog(NetConnect) << "p2p.connect.ingress recving auth from " << socket->remote_endpoint(); - authCipher.resize(307); + clog(NetConnect) << "p2p.connect.ingress recving auth from " << m_socket->remote_endpoint(); + m_authCipher.resize(307); auto self(shared_from_this()); - ba::async_read(*socket, ba::buffer(authCipher, 307), [this, self](boost::system::error_code ec, std::size_t) + ba::async_read(*m_socket, ba::buffer(m_authCipher, 307), [this, self](boost::system::error_code ec, std::size_t) { if (ec) transition(ec); - else if (decryptECIES(host->m_alias.sec(), bytesConstRef(&authCipher), auth)) + else if (decryptECIES(m_host->m_alias.sec(), bytesConstRef(&m_authCipher), m_auth)) { - bytesConstRef sig(&auth[0], Signature::size); - bytesConstRef hepubk(&auth[Signature::size], h256::size); - bytesConstRef pubk(&auth[Signature::size + h256::size], Public::size); - bytesConstRef nonce(&auth[Signature::size + h256::size + Public::size], h256::size); - pubk.copyTo(remote.ref()); - nonce.copyTo(remoteNonce.ref()); + bytesConstRef sig(&m_auth[0], Signature::size); + bytesConstRef hepubk(&m_auth[Signature::size], h256::size); + bytesConstRef pubk(&m_auth[Signature::size + h256::size], Public::size); + bytesConstRef nonce(&m_auth[Signature::size + h256::size + Public::size], h256::size); + pubk.copyTo(m_remote.ref()); + nonce.copyTo(m_remoteNonce.ref()); Secret sharedSecret; - crypto::ecdh::agree(host->m_alias.sec(), remote, sharedSecret); - remoteEphemeral = recover(*(Signature*)sig.data(), sharedSecret ^ remoteNonce); - assert(sha3(remoteEphemeral) == *(h256*)hepubk.data()); + crypto::ecdh::agree(m_host->m_alias.sec(), m_remote, sharedSecret); + m_remoteEphemeral = recover(*(Signature*)sig.data(), sharedSecret ^ m_remoteNonce); + assert(sha3(m_remoteEphemeral) == *(h256*)hepubk.data()); transition(); } else { - clog(NetWarn) << "p2p.connect.egress recving auth decrypt failed for" << socket->remote_endpoint(); - nextState = Error; + clog(NetWarn) << "p2p.connect.egress recving auth decrypt failed for" << m_socket->remote_endpoint(); + m_nextState = Error; transition(); } }); @@ -284,23 +283,23 @@ void RLPXHandshake::readAuth() void RLPXHandshake::readAck() { - clog(NetConnect) << "p2p.connect.egress recving ack from " << socket->remote_endpoint(); - ackCipher.resize(210); + clog(NetConnect) << "p2p.connect.egress recving ack from " << m_socket->remote_endpoint(); + m_ackCipher.resize(210); auto self(shared_from_this()); - ba::async_read(*socket, ba::buffer(ackCipher, 210), [this, self](boost::system::error_code ec, std::size_t) + ba::async_read(*m_socket, ba::buffer(m_ackCipher, 210), [this, self](boost::system::error_code ec, std::size_t) { if (ec) transition(ec); - else if (decryptECIES(host->m_alias.sec(), bytesConstRef(&ackCipher), ack)) + else if (decryptECIES(m_host->m_alias.sec(), bytesConstRef(&m_ackCipher), m_ack)) { - bytesConstRef(&ack).cropped(0, Public::size).copyTo(remoteEphemeral.ref()); - bytesConstRef(&ack).cropped(Public::size, h256::size).copyTo(remoteNonce.ref()); + bytesConstRef(&m_ack).cropped(0, Public::size).copyTo(m_remoteEphemeral.ref()); + bytesConstRef(&m_ack).cropped(Public::size, h256::size).copyTo(m_remoteNonce.ref()); transition(); } else { - clog(NetWarn) << "p2p.connect.egress recving ack decrypt failed for " << socket->remote_endpoint(); - nextState = Error; + clog(NetWarn) << "p2p.connect.egress recving ack decrypt failed for " << m_socket->remote_endpoint(); + m_nextState = Error; transition(); } }); @@ -308,95 +307,95 @@ void RLPXHandshake::readAck() void RLPXHandshake::error() { - clog(NetConnect) << "Disconnecting " << socket->remote_endpoint() << " (Handshake Failed)"; + clog(NetConnect) << "Disconnecting " << m_socket->remote_endpoint() << " (Handshake Failed)"; boost::system::error_code ec; - socket->shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); - if (socket->is_open()) - socket->close(); + m_socket->shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); + if (m_socket->is_open()) + m_socket->close(); } void RLPXHandshake::transition(boost::system::error_code _ech) { - if (_ech || nextState == Error) + if (_ech || m_nextState == Error) return error(); auto self(shared_from_this()); - if (nextState == New) + if (m_nextState == New) { - nextState = AckAuth; - if (originated) + m_nextState = AckAuth; + if (m_originated) writeAuth(); else readAuth(); } - else if (nextState == AckAuth) + else if (m_nextState == AckAuth) { - nextState = WriteHello; - if (originated) + m_nextState = WriteHello; + if (m_originated) readAck(); else writeAck(); } - else if (nextState == WriteHello) + else if (m_nextState == WriteHello) { - nextState = ReadHello; + m_nextState = ReadHello; - if (originated) + if (m_originated) clog(NetConnect) << "p2p.connect.egress sending capabilities handshake"; else clog(NetConnect) << "p2p.connect.ingress sending capabilities handshake"; - io.reset(new RLPXFrameIO(*this)); + m_io.reset(new RLPXFrameIO(*this)); // old packet format // 5 arguments, HelloPacket RLPStream s; s.appendList(5 + 1).append((unsigned)0) - << host->protocolVersion() - << host->m_clientVersion - << host->caps() - << host->m_tcpPublic.port() - << host->id(); + << m_host->protocolVersion() + << m_host->m_clientVersion + << m_host->caps() + << m_host->m_tcpPublic.port() + << m_host->id(); bytes packet; s.swapOut(packet); - io->writeSingleFramePacket(&packet, handshakeOutBuffer); - ba::async_write(*socket, ba::buffer(handshakeOutBuffer), [this, self](boost::system::error_code ec, std::size_t) + m_io->writeSingleFramePacket(&packet, m_handshakeOutBuffer); + ba::async_write(*m_socket, ba::buffer(m_handshakeOutBuffer), [this, self](boost::system::error_code ec, std::size_t) { transition(ec); }); } - else if (nextState == ReadHello) + else if (m_nextState == ReadHello) { // Authenticate and decrypt initial hello frame with initial RLPXFrameIO - // and request host to start session. - nextState = StartSession; + // and request m_host to start session. + m_nextState = StartSession; // read frame header - handshakeInBuffer.resize(h256::size); - ba::async_read(*socket, boost::asio::buffer(handshakeInBuffer, h256::size), [this,self](boost::system::error_code ec, std::size_t length) + m_handshakeInBuffer.resize(h256::size); + ba::async_read(*m_socket, boost::asio::buffer(m_handshakeInBuffer, h256::size), [this,self](boost::system::error_code ec, std::size_t length) { if (ec) transition(ec); else { /// authenticate and decrypt header - if (!io->authAndDecryptHeader(*(h256*)handshakeInBuffer.data())) + if (!m_io->authAndDecryptHeader(*(h256*)m_handshakeInBuffer.data())) { - nextState = Error; + m_nextState = Error; transition(); return; } - clog(NetNote) << (originated ? "p2p.connect.egress" : "p2p.connect.ingress") << "recvd hello header"; + clog(NetNote) << (m_originated ? "p2p.connect.egress" : "p2p.connect.ingress") << "recvd hello header"; /// check frame size - bytes& header = handshakeInBuffer; + bytes& header = m_handshakeInBuffer; uint32_t frameSize = (uint32_t)(header[2]) | (uint32_t)(header[1])<<8 | (uint32_t)(header[0])<<16; if (frameSize > 1024) { // all future frames: 16777216 - clog(NetWarn) << (originated ? "p2p.connect.egress" : "p2p.connect.ingress") << "hello frame is too large"; - nextState = Error; + clog(NetWarn) << (m_originated ? "p2p.connect.egress" : "p2p.connect.ingress") << "hello frame is too large"; + m_nextState = Error; transition(); return; } @@ -406,33 +405,33 @@ void RLPXHandshake::transition(boost::system::error_code _ech) bytesConstRef(&header).cropped(3).copyTo(&headerRLP); /// read padded frame and mac - handshakeInBuffer.resize(frameSize + ((16 - (frameSize % 16)) % 16) + h128::size); - ba::async_read(*socket, boost::asio::buffer(handshakeInBuffer, handshakeInBuffer.size()), [this, self, headerRLP](boost::system::error_code ec, std::size_t length) + m_handshakeInBuffer.resize(frameSize + ((16 - (frameSize % 16)) % 16) + h128::size); + ba::async_read(*m_socket, boost::asio::buffer(m_handshakeInBuffer, m_handshakeInBuffer.size()), [this, self, headerRLP](boost::system::error_code ec, std::size_t length) { if (ec) transition(ec); else { - if (!io->authAndDecryptFrame(bytesRef(&handshakeInBuffer))) + if (!m_io->authAndDecryptFrame(bytesRef(&m_handshakeInBuffer))) { - clog(NetWarn) << (originated ? "p2p.connect.egress" : "p2p.connect.ingress") << "hello frame: decrypt failed"; - nextState = Error; + clog(NetWarn) << (m_originated ? "p2p.connect.egress" : "p2p.connect.ingress") << "hello frame: decrypt failed"; + m_nextState = Error; transition(); return; } - RLP rlp(handshakeInBuffer); + RLP rlp(m_handshakeInBuffer); auto packetType = (PacketType)rlp[0].toInt(); if (packetType != 0) { - clog(NetWarn) << (originated ? "p2p.connect.egress" : "p2p.connect.ingress") << "hello frame: invalid packet type"; - nextState = Error; + clog(NetWarn) << (m_originated ? "p2p.connect.egress" : "p2p.connect.ingress") << "hello frame: invalid packet type"; + m_nextState = Error; transition(); return; } // todo: memory management of RLPFrameIO - host->startPeerSession(remote, rlp, socket); + m_host->startPeerSession(m_remote, rlp, m_socket, m_io.get()); } }); } diff --git a/libp2p/RLPxHandshake.h b/libp2p/RLPxHandshake.h index 2f8cc9094..a50ec2718 100644 --- a/libp2p/RLPxHandshake.h +++ b/libp2p/RLPxHandshake.h @@ -35,10 +35,12 @@ namespace dev namespace p2p { +class Session; class RLPXHandshake; class RLPXFrameIO { + friend class Session; public: RLPXFrameIO(RLPXHandshake const& _init); @@ -74,7 +76,6 @@ private: bi::tcp::socket* m_socket; }; -// TODO: change properties to m_ class RLPXHandshake: public std::enable_shared_from_this { public: @@ -91,12 +92,12 @@ public: }; /// Handshake for ingress connection. Takes ownership of socket. - RLPXHandshake(Host* _host, bi::tcp::socket* _socket): host(_host), socket(std::move(_socket)), originated(false) { crypto::Nonce::get().ref().copyTo(nonce.ref()); } + RLPXHandshake(Host* _host, bi::tcp::socket* _socket): m_host(_host), m_socket(std::move(_socket)), m_originated(false) { crypto::Nonce::get().ref().copyTo(m_nonce.ref()); } /// Handshake for egress connection to _remote. Takes ownership of socket. - RLPXHandshake(Host* _host, bi::tcp::socket* _socket, NodeId _remote): host(_host), remote(_remote), socket(std::move(_socket)), originated(true) { crypto::Nonce::get().ref().copyTo(nonce.ref()); } + RLPXHandshake(Host* _host, bi::tcp::socket* _socket, NodeId _remote): m_host(_host), m_remote(_remote), m_socket(std::move(_socket)), m_originated(true) { crypto::Nonce::get().ref().copyTo(m_nonce.ref()); } - ~RLPXHandshake() { delete socket; } + ~RLPXHandshake() { delete m_socket; } void start() { transition(); } @@ -111,32 +112,32 @@ protected: void transition(boost::system::error_code _ech = boost::system::error_code()); /// Current state of handshake. - State nextState = New; + State m_nextState = New; - Host* host; + Host* m_host; /// Node id of remote host for socket. - NodeId remote; + NodeId m_remote; - bi::tcp::socket* socket; - bool originated = false; + bi::tcp::socket* m_socket; + bool m_originated = false; /// Buffers for encoded and decoded handshake phases - bytes auth; - bytes authCipher; - bytes ack; - bytes ackCipher; - bytes handshakeOutBuffer; - bytes handshakeInBuffer; + bytes m_auth; + bytes m_authCipher; + bytes m_ack; + bytes m_ackCipher; + bytes m_handshakeOutBuffer; + bytes m_handshakeInBuffer; - crypto::ECDHE ecdhe; - h256 nonce; + crypto::ECDHE m_ecdhe; + h256 m_nonce; - Public remoteEphemeral; - h256 remoteNonce; + Public m_remoteEphemeral; + h256 m_remoteNonce; /// Frame IO is used to read frame for last step of handshake authentication. - std::unique_ptr io; + std::unique_ptr m_io; }; } diff --git a/libp2p/Session.cpp b/libp2p/Session.cpp index 310ecea61..e11e70e35 100644 --- a/libp2p/Session.cpp +++ b/libp2p/Session.cpp @@ -37,9 +37,11 @@ using namespace dev::p2p; #endif #define clogS(X) dev::LogOutputStream(false) << "| " << std::setw(2) << m_socket.native_handle() << "] " -Session::Session(Host* _s, bi::tcp::socket _socket, std::shared_ptr const& _n, PeerSessionInfo _info): +Session::Session(Host* _s, RLPXFrameIO _io, std::shared_ptr const& _n, PeerSessionInfo _info): m_server(_s), - m_socket(move(_socket)), +#warning fixme + m_socket(move(*_io.m_socket)), + m_io(move(_io)), m_peer(_n), m_info(_info), m_ping(chrono::steady_clock::time_point::max()) diff --git a/libp2p/Session.h b/libp2p/Session.h index 9f87d386d..5767b7756 100644 --- a/libp2p/Session.h +++ b/libp2p/Session.h @@ -32,6 +32,7 @@ #include #include #include +#include "RLPxHandshake.h" #include "Common.h" namespace dev @@ -52,7 +53,7 @@ class Session: public std::enable_shared_from_this friend class HostCapabilityFace; public: - Session(Host* _server, bi::tcp::socket _socket, std::shared_ptr const& _n, PeerSessionInfo _info); + Session(Host* _server, RLPXFrameIO _io, std::shared_ptr const& _n, PeerSessionInfo _info); virtual ~Session(); void start(); @@ -63,7 +64,7 @@ public: bool isConnected() const { return m_socket.is_open(); } NodeId id() const; - unsigned socketId() const { return m_socket.native_handle(); } + unsigned socketId() const { return m_info.socket; } template std::shared_ptr cap() const { try { return std::static_pointer_cast(m_capabilities.at(std::make_pair(PeerCap::name(), PeerCap::version()))); } catch (...) { return nullptr; } } @@ -104,6 +105,7 @@ private: Host* m_server; ///< The host that owns us. Never null. mutable bi::tcp::socket m_socket; ///< Socket for the peer's connection. Mutable to ask for native_handle(). + RLPXFrameIO m_io; ///< Transport over which packets are sent. Mutex x_writeQueue; ///< Mutex for the write queue. std::deque m_writeQueue; ///< The write queue. std::array m_data; ///< Buffer for ingress packet data. From a0aaf614f37ecc3acc4252c1e4c06e48e5ddaf88 Mon Sep 17 00:00:00 2001 From: subtly Date: Fri, 6 Mar 2015 03:14:38 +0100 Subject: [PATCH 030/100] sharedptr wrapper class for socket --- libp2p/Host.cpp | 32 ++---- libp2p/Host.h | 6 +- libp2p/RLPxFrameIO.cpp | 206 +++++++++++++++++++++++++++++++++++++ libp2p/RLPxFrameIO.h | 97 ++++++++++++++++++ libp2p/RLPxHandshake.cpp | 216 ++++----------------------------------- libp2p/RLPxHandshake.h | 60 ++--------- libp2p/Session.cpp | 4 +- libp2p/Session.h | 3 +- 8 files changed, 348 insertions(+), 276 deletions(-) create mode 100644 libp2p/RLPxFrameIO.cpp create mode 100644 libp2p/RLPxFrameIO.h diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index ef2f95180..057453d8f 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -157,7 +157,7 @@ unsigned Host::protocolVersion() const return 4; } -bool Host::startPeerSession(Public const& _id, RLP const& _rlp, bi::tcp::socket *_socket, RLPXFrameIO* _io) +bool Host::startPeerSession(Public const& _id, RLP const& _rlp, RLPXFrameIO* _io, bi::tcp::endpoint _endpoint) { /// Get or create Peer shared_ptr p; @@ -172,7 +172,7 @@ bool Host::startPeerSession(Public const& _id, RLP const& _rlp, bi::tcp::socket p->m_lastConnected = std::chrono::system_clock::now(); p->m_failedAttempts = 0; // TODO: update pendingconns w/session-weak-ptr for graceful shutdown (otherwise this line isn't safe) - p->endpoint.tcp.address(_socket->remote_endpoint().address()); + p->endpoint.tcp.address(_endpoint.address()); auto protocolVersion = _rlp[1].toInt(); auto clientVersion = _rlp[2].toString(); @@ -187,7 +187,7 @@ bool Host::startPeerSession(Public const& _id, RLP const& _rlp, bi::tcp::socket clog(NetMessageSummary) << "Hello: " << clientVersion << "V[" << protocolVersion << "]" << _id.abridged() << showbase << capslog.str() << dec << listenPort; // create session so disconnects are managed - auto ps = make_shared(this, move(*_io), p, PeerSessionInfo({_id, clientVersion, _socket->remote_endpoint().address().to_string(), listenPort, chrono::steady_clock::duration(), _rlp[3].toSet(), 0, map()})); + auto ps = make_shared(this, move(*_io), p, PeerSessionInfo({_id, clientVersion, _endpoint.address().to_string(), listenPort, chrono::steady_clock::duration(), _rlp[3].toSet(), 0, map()})); if (protocolVersion != this->protocolVersion()) { ps->disconnect(IncompatibleProtocol); @@ -368,8 +368,8 @@ void Host::runAcceptor() // case the socket may be open and must be closed to prevent asio from // processing socket events after socket is deallocated. - bi::tcp::socket *s = new bi::tcp::socket(m_ioService); - m_tcp4Acceptor.async_accept(*s, [=](boost::system::error_code ec) + auto socket = make_shared(new bi::tcp::socket(m_ioService)); + m_tcp4Acceptor.async_accept(socket->ref(), [=](boost::system::error_code ec) { // if no error code bool success = false; @@ -378,7 +378,7 @@ void Host::runAcceptor() try { // incoming connection; we don't yet know nodeid - auto handshake = make_shared(this, s); + auto handshake = make_shared(this, socket); handshake->start(); success = true; } @@ -392,18 +392,9 @@ void Host::runAcceptor() } } - // asio doesn't close socket on error if (!success) - { - if (s->is_open()) - { - boost::system::error_code ec; - s->shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); - s->close(); - } - delete s; - } - + socket->ref().close(); + m_accepting = false; if (ec.value() < 1) runAcceptor(); @@ -485,20 +476,19 @@ void Host::connect(std::shared_ptr const& _p) } clog(NetConnect) << "Attempting connection to node" << _p->id.abridged() << "@" << _p->peerEndpoint() << "from" << id().abridged(); - bi::tcp::socket* s = new bi::tcp::socket(m_ioService); - s->async_connect(_p->peerEndpoint(), [=](boost::system::error_code const& ec) + auto socket = make_shared(new bi::tcp::socket(m_ioService)); + socket->ref().async_connect(_p->peerEndpoint(), [=](boost::system::error_code const& ec) { if (ec) { clog(NetConnect) << "Connection refused to node" << _p->id.abridged() << "@" << _p->peerEndpoint() << "(" << ec.message() << ")"; _p->m_lastDisconnect = TCPError; _p->m_lastAttempted = std::chrono::system_clock::now(); - delete s; } else { clog(NetConnect) << "Connected to" << _p->id.abridged() << "@" << _p->peerEndpoint(); - auto handshake = make_shared(this, s, _p->id); + auto handshake = make_shared(this, socket, _p->id); handshake->start(); } Guard l(x_pendingNodeConns); diff --git a/libp2p/Host.h b/libp2p/Host.h index a5091ae7e..55f4d61b0 100644 --- a/libp2p/Host.h +++ b/libp2p/Host.h @@ -40,7 +40,7 @@ #include "HostCapability.h" #include "Network.h" #include "Peer.h" -#include "RLPxHandshake.h" +#include "RLPxFrameIO.h" #include "Common.h" namespace ba = boost::asio; namespace bi = ba::ip; @@ -150,8 +150,8 @@ public: NodeId id() const { return m_alias.pub(); } - /// Validates and starts peer session, taking ownership of _socket. Disconnects and returns false upon error. - bool startPeerSession(Public const& _id, RLP const& _hello, bi::tcp::socket* _socket, RLPXFrameIO* _io); + /// Validates and starts peer session, taking ownership of _io. Disconnects and returns false upon error. + bool startPeerSession(Public const& _id, RLP const& _hello, RLPXFrameIO* _io, bi::tcp::endpoint _endpoint); protected: void onNodeTableEvent(NodeId const& _n, NodeTableEventType const& _e); diff --git a/libp2p/RLPxFrameIO.cpp b/libp2p/RLPxFrameIO.cpp new file mode 100644 index 000000000..3ddaf9143 --- /dev/null +++ b/libp2p/RLPxFrameIO.cpp @@ -0,0 +1,206 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . + */ +/** @file RLPXFrameIO.cpp + * @author Alex Leverington + * @date 2015 + */ + +#include "Host.h" +#include "Session.h" +#include "Peer.h" +#include "RLPxHandshake.h" +#include "RLPxFrameIO.h" +using namespace std; +using namespace dev; +using namespace dev::p2p; +using namespace CryptoPP; + +RLPXFrameIO::RLPXFrameIO(RLPXHandshake const& _init): m_socket(_init.m_socket) +{ + // we need: + // originated? + // Secret == output of ecdhe agreement + // authCipher + // ackCipher + + bytes keyMaterialBytes(64); + bytesRef keyMaterial(&keyMaterialBytes); + + // shared-secret = sha3(ecdhe-shared-secret || sha3(nonce || initiator-nonce)) + Secret ephemeralShared; + _init.m_ecdhe.agree(_init.m_remoteEphemeral, ephemeralShared); + ephemeralShared.ref().copyTo(keyMaterial.cropped(0, h256::size)); + h512 nonceMaterial; + h256 const& leftNonce = _init.m_originated ? _init.m_remoteNonce : _init.m_nonce; + h256 const& rightNonce = _init.m_originated ? _init.m_nonce : _init.m_remoteNonce; + leftNonce.ref().copyTo(nonceMaterial.ref().cropped(0, h256::size)); + rightNonce.ref().copyTo(nonceMaterial.ref().cropped(h256::size, h256::size)); + auto outRef(keyMaterial.cropped(h256::size, h256::size)); + sha3(nonceMaterial.ref(), outRef); // output h(nonces) + + sha3(keyMaterial, outRef); // output shared-secret + // token: sha3(outRef, bytesRef(&token)); -> m_host (to be saved to disk) + + // aes-secret = sha3(ecdhe-shared-secret || shared-secret) + sha3(keyMaterial, outRef); // output aes-secret + m_frameEnc.SetKeyWithIV(outRef.data(), h128::size, h128().data()); + m_frameDec.SetKeyWithIV(outRef.data(), h128::size, h128().data()); + + // mac-secret = sha3(ecdhe-shared-secret || aes-secret) + sha3(keyMaterial, outRef); // output mac-secret + m_macEnc.SetKey(outRef.data(), h128::size); + + // Initiator egress-mac: sha3(mac-secret^recipient-nonce || auth-sent-init) + // ingress-mac: sha3(mac-secret^initiator-nonce || auth-recvd-ack) + // Recipient egress-mac: sha3(mac-secret^initiator-nonce || auth-sent-ack) + // ingress-mac: sha3(mac-secret^recipient-nonce || auth-recvd-init) + + (*(h256*)outRef.data() ^ _init.m_remoteNonce).ref().copyTo(keyMaterial); + bytes const& egressCipher = _init.m_originated ? _init.m_authCipher : _init.m_ackCipher; + keyMaterialBytes.resize(h256::size + egressCipher.size()); + keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); + bytesConstRef(&egressCipher).copyTo(keyMaterial.cropped(h256::size, egressCipher.size())); + m_egressMac.Update(keyMaterial.data(), keyMaterial.size()); + + // recover mac-secret by re-xoring remoteNonce + (*(h256*)keyMaterial.data() ^ _init.m_remoteNonce ^ _init.m_nonce).ref().copyTo(keyMaterial); + bytes const& ingressCipher = _init.m_originated ? _init.m_ackCipher : _init.m_authCipher; + keyMaterialBytes.resize(h256::size + ingressCipher.size()); + keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); + bytesConstRef(&ingressCipher).copyTo(keyMaterial.cropped(h256::size, ingressCipher.size())); + m_ingressMac.Update(keyMaterial.data(), keyMaterial.size()); +} + +void RLPXFrameIO::writeSingleFramePacket(bytesConstRef _packet, bytes& o_bytes) +{ + // _packet = type || rlpList() + + // current/old packet format: prep(_s).appendList(_args + 1).append((unsigned)_id); + RLPStream header; + header.appendRaw(bytes({byte(_packet.size() >> 16), byte(_packet.size() >> 8), byte(_packet.size())})); + // zeroHeader: []byte{0xC2, 0x80, 0x80}. Should be rlpList(protocolType,seqId,totalPacketSize). + header.appendRaw(bytes({0xc2,0x80,0x80})); + + // TODO: SECURITY check that header is <= 16 bytes + + bytes headerWithMac; + header.swapOut(headerWithMac); + headerWithMac.resize(32); + m_frameEnc.ProcessData(headerWithMac.data(), headerWithMac.data(), 16); + updateEgressMACWithHeader(bytesConstRef(&headerWithMac).cropped(0, 16)); + egressDigest().ref().copyTo(bytesRef(&headerWithMac).cropped(h128::size,h128::size)); + + auto padding = (16 - (_packet.size() % 16)) % 16; + o_bytes.swap(headerWithMac); + o_bytes.resize(32 + _packet.size() + padding + h128::size); + bytesRef packetRef(o_bytes.data() + 32, _packet.size()); + m_frameEnc.ProcessData(packetRef.data(), _packet.data(), _packet.size()); + bytesRef paddingRef(o_bytes.data() + 32 + _packet.size(), padding); + if (padding) + m_frameEnc.ProcessData(paddingRef.data(), paddingRef.data(), padding); + bytesRef packetWithPaddingRef(o_bytes.data() + 32, _packet.size() + padding); + updateEgressMACWithEndOfFrame(packetWithPaddingRef); + bytesRef macRef(o_bytes.data() + 32 + _packet.size() + padding, h128::size); + egressDigest().ref().copyTo(macRef); + clog(NetConnect) << "SENT FRAME " << _packet.size() << *(h128*)macRef.data(); + clog(NetConnect) << "FRAME TAIL " << *(h128*)(o_bytes.data() + 32 + _packet.size() + padding); +} + +bool RLPXFrameIO::authAndDecryptHeader(h256& io) +{ + updateIngressMACWithHeader(io.ref()); + bytesConstRef macRef = io.ref().cropped(h128::size, h128::size); + if (*(h128*)macRef.data() != ingressDigest()) + return false; + m_frameDec.ProcessData(io.data(), io.data(), 16); + return true; +} + +bool RLPXFrameIO::authAndDecryptFrame(bytesRef io) +{ + bytesRef cipherText(io.cropped(0, io.size() - h128::size)); + updateIngressMACWithEndOfFrame(cipherText); + bytesConstRef frameMac(io.data() + io.size() - h128::size, h128::size); + if (*(h128*)frameMac.data() != ingressDigest()) + return false; + m_frameDec.ProcessData(io.data(), io.data(), io.size() - h128::size); + return true; +} + +h128 RLPXFrameIO::egressDigest() +{ + SHA3_256 h(m_egressMac); + h128 digest; + h.TruncatedFinal(digest.data(), h128::size); + return move(digest); +} + +h128 RLPXFrameIO::ingressDigest() +{ + SHA3_256 h(m_ingressMac); + h128 digest; + h.TruncatedFinal(digest.data(), h128::size); + return move(digest); +} + +void RLPXFrameIO::updateEgressMACWithHeader(bytesConstRef _headerCipher) +{ + updateMAC(m_egressMac, *(h128*)_headerCipher.data()); +} + +void RLPXFrameIO::updateEgressMACWithEndOfFrame(bytesConstRef _cipher) +{ + m_egressMac.Update(_cipher.data(), _cipher.size()); + updateMAC(m_egressMac); + { + SHA3_256 prev(m_egressMac); + h128 digest; + prev.TruncatedFinal(digest.data(), h128::size); + clog(NetConnect) << "EGRESS FRAMEMAC " << _cipher.size() << digest; + } +} + +void RLPXFrameIO::updateIngressMACWithHeader(bytesConstRef _headerCipher) +{ + updateMAC(m_ingressMac, *(h128*)_headerCipher.data()); +} + +void RLPXFrameIO::updateIngressMACWithEndOfFrame(bytesConstRef _cipher) +{ + m_ingressMac.Update(_cipher.data(), _cipher.size()); + updateMAC(m_ingressMac); + { + SHA3_256 prev(m_ingressMac); + h128 digest; + prev.TruncatedFinal(digest.data(), h128::size); + clog(NetConnect) << "INGRESS FRAMEMAC " << _cipher.size() << digest; + } +} + +void RLPXFrameIO::updateMAC(SHA3_256& _mac, h128 const& _seed) +{ + SHA3_256 prevDigest(_mac); + h128 prevDigestOut; + prevDigest.TruncatedFinal(prevDigestOut.data(), h128::size); + + h128 encDigest; + m_macEnc.ProcessData(encDigest.data(), prevDigestOut.data(), h128::size); + encDigest ^= (!!_seed ? _seed : prevDigestOut); + + // update mac for final digest + _mac.Update(encDigest.data(), h128::size); +} diff --git a/libp2p/RLPxFrameIO.h b/libp2p/RLPxFrameIO.h new file mode 100644 index 000000000..07211d4de --- /dev/null +++ b/libp2p/RLPxFrameIO.h @@ -0,0 +1,97 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . + */ +/** @file RLPXFrameIO.h + * @author Alex Leverington + * @date 2015 + */ + + +#pragma once + +#include +#include +#include +#include +#include "Common.h" +namespace ba = boost::asio; +namespace bi = boost::asio::ip; + +namespace dev +{ +namespace p2p +{ + +class RLPXHandshake; + +class RLPXSocket: public std::enable_shared_from_this +{ +public: + RLPXSocket(bi::tcp::socket* _socket): m_socket(std::move(*_socket)) {} + ~RLPXSocket() { close(); } + + bool isConnected() const { return m_socket.is_open(); } + void close() { try { boost::system::error_code ec; m_socket.shutdown(bi::tcp::socket::shutdown_both, ec); if (m_socket.is_open()) m_socket.close(); } catch (...){} } + bi::tcp::endpoint remoteEndpoint() { try { return m_socket.remote_endpoint(); } catch (...){ return bi::tcp::endpoint(); } } + bi::tcp::socket& ref() { return m_socket; } + +protected: + bi::tcp::socket m_socket; +}; + +class RLPXFrameIO +{ + friend class Session; +public: + RLPXFrameIO(RLPXHandshake const& _init); + + void writeSingleFramePacket(bytesConstRef _packet, bytes& o_bytes); + + /// Authenticates and decrypts header in-place. + bool authAndDecryptHeader(h256& io_cipherWithMac); + + /// Authenticates and decrypts frame in-place. + bool authAndDecryptFrame(bytesRef io_cipherWithMac); + + h128 egressDigest(); + + h128 ingressDigest(); + + void updateEgressMACWithHeader(bytesConstRef _headerCipher); + + void updateEgressMACWithEndOfFrame(bytesConstRef _cipher); + + void updateIngressMACWithHeader(bytesConstRef _headerCipher); + + void updateIngressMACWithEndOfFrame(bytesConstRef _cipher); + +protected: + bi::tcp::socket& socket() { return m_socket->ref(); } + +private: + void updateMAC(CryptoPP::SHA3_256& _mac, h128 const& _seed = h128()); + + CryptoPP::CTR_Mode::Encryption m_frameEnc; + CryptoPP::CTR_Mode::Encryption m_frameDec; + CryptoPP::ECB_Mode::Encryption m_macEnc; + CryptoPP::SHA3_256 m_egressMac; + CryptoPP::SHA3_256 m_ingressMac; + + std::shared_ptr m_socket; +}; + +} +} \ No newline at end of file diff --git a/libp2p/RLPxHandshake.cpp b/libp2p/RLPxHandshake.cpp index 8bec9b6dd..bca1afd61 100644 --- a/libp2p/RLPxHandshake.cpp +++ b/libp2p/RLPxHandshake.cpp @@ -28,185 +28,9 @@ using namespace dev; using namespace dev::p2p; using namespace CryptoPP; -RLPXFrameIO::RLPXFrameIO(RLPXHandshake const& _init): m_socket(_init.m_socket) -{ - // we need: - // originated? - // Secret == output of ecdhe agreement - // authCipher - // ackCipher - - bytes keyMaterialBytes(64); - bytesRef keyMaterial(&keyMaterialBytes); - - // shared-secret = sha3(ecdhe-shared-secret || sha3(nonce || initiator-nonce)) - Secret ephemeralShared; - _init.m_ecdhe.agree(_init.m_remoteEphemeral, ephemeralShared); - ephemeralShared.ref().copyTo(keyMaterial.cropped(0, h256::size)); - h512 nonceMaterial; - h256 const& leftNonce = _init.m_originated ? _init.m_remoteNonce : _init.m_nonce; - h256 const& rightNonce = _init.m_originated ? _init.m_nonce : _init.m_remoteNonce; - leftNonce.ref().copyTo(nonceMaterial.ref().cropped(0, h256::size)); - rightNonce.ref().copyTo(nonceMaterial.ref().cropped(h256::size, h256::size)); - auto outRef(keyMaterial.cropped(h256::size, h256::size)); - sha3(nonceMaterial.ref(), outRef); // output h(nonces) - - sha3(keyMaterial, outRef); // output shared-secret - // token: sha3(outRef, bytesRef(&token)); -> m_host (to be saved to disk) - - // aes-secret = sha3(ecdhe-shared-secret || shared-secret) - sha3(keyMaterial, outRef); // output aes-secret - m_frameEnc.SetKeyWithIV(outRef.data(), h128::size, h128().data()); - m_frameDec.SetKeyWithIV(outRef.data(), h128::size, h128().data()); - - // mac-secret = sha3(ecdhe-shared-secret || aes-secret) - sha3(keyMaterial, outRef); // output mac-secret - m_macEnc.SetKey(outRef.data(), h128::size); - - // Initiator egress-mac: sha3(mac-secret^recipient-nonce || auth-sent-init) - // ingress-mac: sha3(mac-secret^initiator-nonce || auth-recvd-ack) - // Recipient egress-mac: sha3(mac-secret^initiator-nonce || auth-sent-ack) - // ingress-mac: sha3(mac-secret^recipient-nonce || auth-recvd-init) - - (*(h256*)outRef.data() ^ _init.m_remoteNonce).ref().copyTo(keyMaterial); - bytes const& egressCipher = _init.m_originated ? _init.m_authCipher : _init.m_ackCipher; - keyMaterialBytes.resize(h256::size + egressCipher.size()); - keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); - bytesConstRef(&egressCipher).copyTo(keyMaterial.cropped(h256::size, egressCipher.size())); - m_egressMac.Update(keyMaterial.data(), keyMaterial.size()); - - // recover mac-secret by re-xoring remoteNonce - (*(h256*)keyMaterial.data() ^ _init.m_remoteNonce ^ _init.m_nonce).ref().copyTo(keyMaterial); - bytes const& ingressCipher = _init.m_originated ? _init.m_ackCipher : _init.m_authCipher; - keyMaterialBytes.resize(h256::size + ingressCipher.size()); - keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); - bytesConstRef(&ingressCipher).copyTo(keyMaterial.cropped(h256::size, ingressCipher.size())); - m_ingressMac.Update(keyMaterial.data(), keyMaterial.size()); -} - -void RLPXFrameIO::writeSingleFramePacket(bytesConstRef _packet, bytes& o_bytes) -{ - // _packet = type || rlpList() - - // current/old packet format: prep(_s).appendList(_args + 1).append((unsigned)_id); - RLPStream header; - header.appendRaw(bytes({byte(_packet.size() >> 16), byte(_packet.size() >> 8), byte(_packet.size())})); - // zeroHeader: []byte{0xC2, 0x80, 0x80}. Should be rlpList(protocolType,seqId,totalPacketSize). - header.appendRaw(bytes({0xc2,0x80,0x80})); - - // TODO: SECURITY check that header is <= 16 bytes - - bytes headerWithMac; - header.swapOut(headerWithMac); - headerWithMac.resize(32); - m_frameEnc.ProcessData(headerWithMac.data(), headerWithMac.data(), 16); - updateEgressMACWithHeader(bytesConstRef(&headerWithMac).cropped(0, 16)); - egressDigest().ref().copyTo(bytesRef(&headerWithMac).cropped(h128::size,h128::size)); - - auto padding = (16 - (_packet.size() % 16)) % 16; - o_bytes.swap(headerWithMac); - o_bytes.resize(32 + _packet.size() + padding + h128::size); - bytesRef packetRef(o_bytes.data() + 32, _packet.size()); - m_frameEnc.ProcessData(packetRef.data(), _packet.data(), _packet.size()); - bytesRef paddingRef(o_bytes.data() + 32 + _packet.size(), padding); - if (padding) - m_frameEnc.ProcessData(paddingRef.data(), paddingRef.data(), padding); - bytesRef packetWithPaddingRef(o_bytes.data() + 32, _packet.size() + padding); - updateEgressMACWithEndOfFrame(packetWithPaddingRef); - bytesRef macRef(o_bytes.data() + 32 + _packet.size() + padding, h128::size); - egressDigest().ref().copyTo(macRef); - clog(NetConnect) << "SENT FRAME " << _packet.size() << *(h128*)macRef.data(); - clog(NetConnect) << "FRAME TAIL " << *(h128*)(o_bytes.data() + 32 + _packet.size() + padding); -} - -bool RLPXFrameIO::authAndDecryptHeader(h256& io) -{ - updateIngressMACWithHeader(io.ref()); - bytesConstRef macRef = io.ref().cropped(h128::size, h128::size); - if (*(h128*)macRef.data() != ingressDigest()) - return false; - m_frameDec.ProcessData(io.data(), io.data(), 16); - return true; -} - -bool RLPXFrameIO::authAndDecryptFrame(bytesRef io) -{ - bytesRef cipherText(io.cropped(0, io.size() - h128::size)); - updateIngressMACWithEndOfFrame(cipherText); - bytesConstRef frameMac(io.data() + io.size() - h128::size, h128::size); - if (*(h128*)frameMac.data() != ingressDigest()) - return false; - m_frameDec.ProcessData(io.data(), io.data(), io.size() - h128::size); - return true; -} - -h128 RLPXFrameIO::egressDigest() -{ - SHA3_256 h(m_egressMac); - h128 digest; - h.TruncatedFinal(digest.data(), h128::size); - return move(digest); -} - -h128 RLPXFrameIO::ingressDigest() -{ - SHA3_256 h(m_ingressMac); - h128 digest; - h.TruncatedFinal(digest.data(), h128::size); - return move(digest); -} - -void RLPXFrameIO::updateEgressMACWithHeader(bytesConstRef _headerCipher) -{ - updateMAC(m_egressMac, *(h128*)_headerCipher.data()); -} - -void RLPXFrameIO::updateEgressMACWithEndOfFrame(bytesConstRef _cipher) -{ - m_egressMac.Update(_cipher.data(), _cipher.size()); - updateMAC(m_egressMac); - { - SHA3_256 prev(m_egressMac); - h128 digest; - prev.TruncatedFinal(digest.data(), h128::size); - clog(NetConnect) << "EGRESS FRAMEMAC " << _cipher.size() << digest; - } -} - -void RLPXFrameIO::updateIngressMACWithHeader(bytesConstRef _headerCipher) -{ - updateMAC(m_ingressMac, *(h128*)_headerCipher.data()); -} - -void RLPXFrameIO::updateIngressMACWithEndOfFrame(bytesConstRef _cipher) -{ - m_ingressMac.Update(_cipher.data(), _cipher.size()); - updateMAC(m_ingressMac); - { - SHA3_256 prev(m_ingressMac); - h128 digest; - prev.TruncatedFinal(digest.data(), h128::size); - clog(NetConnect) << "INGRESS FRAMEMAC " << _cipher.size() << digest; - } -} - -void RLPXFrameIO::updateMAC(SHA3_256& _mac, h128 const& _seed) -{ - SHA3_256 prevDigest(_mac); - h128 prevDigestOut; - prevDigest.TruncatedFinal(prevDigestOut.data(), h128::size); - - h128 encDigest; - m_macEnc.ProcessData(encDigest.data(), prevDigestOut.data(), h128::size); - encDigest ^= (!!_seed ? _seed : prevDigestOut); - - // update mac for final digest - _mac.Update(encDigest.data(), h128::size); -} - void RLPXHandshake::writeAuth() { - clog(NetConnect) << "p2p.connect.egress sending auth to " << m_socket->remote_endpoint(); + clog(NetConnect) << "p2p.connect.egress sending auth to " << m_socket->remoteEndpoint(); m_auth.resize(Signature::size + h256::size + Public::size + h256::size + 1); bytesRef sig(&m_auth[0], Signature::size); bytesRef hepubk(&m_auth[Signature::size], h256::size); @@ -224,7 +48,7 @@ void RLPXHandshake::writeAuth() encryptECIES(m_remote, &m_auth, m_authCipher); auto self(shared_from_this()); - ba::async_write(*m_socket, ba::buffer(m_authCipher), [this, self](boost::system::error_code ec, std::size_t) + ba::async_write(m_socket->ref(), ba::buffer(m_authCipher), [this, self](boost::system::error_code ec, std::size_t) { transition(ec); }); @@ -232,7 +56,7 @@ void RLPXHandshake::writeAuth() void RLPXHandshake::writeAck() { - clog(NetConnect) << "p2p.connect.ingress sending ack to " << m_socket->remote_endpoint(); + clog(NetConnect) << "p2p.connect.ingress sending ack to " << m_socket->remoteEndpoint(); m_ack.resize(Public::size + h256::size + 1); bytesRef epubk(&m_ack[0], Public::size); bytesRef nonce(&m_ack[Public::size], h256::size); @@ -242,7 +66,7 @@ void RLPXHandshake::writeAck() encryptECIES(m_remote, &m_ack, m_ackCipher); auto self(shared_from_this()); - ba::async_write(*m_socket, ba::buffer(m_ackCipher), [this, self](boost::system::error_code ec, std::size_t) + ba::async_write(m_socket->ref(), ba::buffer(m_ackCipher), [this, self](boost::system::error_code ec, std::size_t) { transition(ec); }); @@ -250,10 +74,10 @@ void RLPXHandshake::writeAck() void RLPXHandshake::readAuth() { - clog(NetConnect) << "p2p.connect.ingress recving auth from " << m_socket->remote_endpoint(); + clog(NetConnect) << "p2p.connect.ingress recving auth from " << m_socket->remoteEndpoint(); m_authCipher.resize(307); auto self(shared_from_this()); - ba::async_read(*m_socket, ba::buffer(m_authCipher, 307), [this, self](boost::system::error_code ec, std::size_t) + ba::async_read(m_socket->ref(), ba::buffer(m_authCipher, 307), [this, self](boost::system::error_code ec, std::size_t) { if (ec) transition(ec); @@ -274,7 +98,7 @@ void RLPXHandshake::readAuth() } else { - clog(NetWarn) << "p2p.connect.egress recving auth decrypt failed for" << m_socket->remote_endpoint(); + clog(NetWarn) << "p2p.connect.egress recving auth decrypt failed for" << m_socket->remoteEndpoint(); m_nextState = Error; transition(); } @@ -283,10 +107,10 @@ void RLPXHandshake::readAuth() void RLPXHandshake::readAck() { - clog(NetConnect) << "p2p.connect.egress recving ack from " << m_socket->remote_endpoint(); + clog(NetConnect) << "p2p.connect.egress recving ack from " << m_socket->remoteEndpoint(); m_ackCipher.resize(210); auto self(shared_from_this()); - ba::async_read(*m_socket, ba::buffer(m_ackCipher, 210), [this, self](boost::system::error_code ec, std::size_t) + ba::async_read(m_socket->ref(), ba::buffer(m_ackCipher, 210), [this, self](boost::system::error_code ec, std::size_t) { if (ec) transition(ec); @@ -298,7 +122,7 @@ void RLPXHandshake::readAck() } else { - clog(NetWarn) << "p2p.connect.egress recving ack decrypt failed for " << m_socket->remote_endpoint(); + clog(NetWarn) << "p2p.connect.egress recving ack decrypt failed for " << m_socket->remoteEndpoint(); m_nextState = Error; transition(); } @@ -307,11 +131,8 @@ void RLPXHandshake::readAck() void RLPXHandshake::error() { - clog(NetConnect) << "Disconnecting " << m_socket->remote_endpoint() << " (Handshake Failed)"; - boost::system::error_code ec; - m_socket->shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); - if (m_socket->is_open()) - m_socket->close(); + clog(NetConnect) << "Disconnecting " << m_socket->remoteEndpoint() << " (Handshake Failed)"; + m_socket->close(); } void RLPXHandshake::transition(boost::system::error_code _ech) @@ -344,7 +165,7 @@ void RLPXHandshake::transition(boost::system::error_code _ech) clog(NetConnect) << "p2p.connect.egress sending capabilities handshake"; else clog(NetConnect) << "p2p.connect.ingress sending capabilities handshake"; - + m_io.reset(new RLPXFrameIO(*this)); // old packet format @@ -359,7 +180,7 @@ void RLPXHandshake::transition(boost::system::error_code _ech) bytes packet; s.swapOut(packet); m_io->writeSingleFramePacket(&packet, m_handshakeOutBuffer); - ba::async_write(*m_socket, ba::buffer(m_handshakeOutBuffer), [this, self](boost::system::error_code ec, std::size_t) + ba::async_write(m_socket->ref(), ba::buffer(m_handshakeOutBuffer), [this, self](boost::system::error_code ec, std::size_t) { transition(ec); }); @@ -372,7 +193,7 @@ void RLPXHandshake::transition(boost::system::error_code _ech) // read frame header m_handshakeInBuffer.resize(h256::size); - ba::async_read(*m_socket, boost::asio::buffer(m_handshakeInBuffer, h256::size), [this,self](boost::system::error_code ec, std::size_t length) + ba::async_read(m_socket->ref(), boost::asio::buffer(m_handshakeInBuffer, h256::size), [this,self](boost::system::error_code ec, std::size_t length) { if (ec) transition(ec); @@ -406,7 +227,7 @@ void RLPXHandshake::transition(boost::system::error_code _ech) /// read padded frame and mac m_handshakeInBuffer.resize(frameSize + ((16 - (frameSize % 16)) % 16) + h128::size); - ba::async_read(*m_socket, boost::asio::buffer(m_handshakeInBuffer, m_handshakeInBuffer.size()), [this, self, headerRLP](boost::system::error_code ec, std::size_t length) + ba::async_read(m_socket->ref(), boost::asio::buffer(m_handshakeInBuffer, m_handshakeInBuffer.size()), [this, self, headerRLP](boost::system::error_code ec, std::size_t length) { if (ec) transition(ec); @@ -429,9 +250,8 @@ void RLPXHandshake::transition(boost::system::error_code _ech) transition(); return; } - - // todo: memory management of RLPFrameIO - m_host->startPeerSession(m_remote, rlp, m_socket, m_io.get()); + + m_host->startPeerSession(m_remote, rlp, m_io.get(), m_socket->remoteEndpoint()); } }); } diff --git a/libp2p/RLPxHandshake.h b/libp2p/RLPxHandshake.h index a50ec2718..12abe4379 100644 --- a/libp2p/RLPxHandshake.h +++ b/libp2p/RLPxHandshake.h @@ -26,6 +26,7 @@ #include #include #include +#include "RLPxFrameIO.h" #include "Common.h" namespace ba = boost::asio; namespace bi = boost::asio::ip; @@ -35,52 +36,9 @@ namespace dev namespace p2p { -class Session; -class RLPXHandshake; - -class RLPXFrameIO -{ - friend class Session; -public: - RLPXFrameIO(RLPXHandshake const& _init); - - void writeSingleFramePacket(bytesConstRef _packet, bytes& o_bytes); - - /// Authenticates and decrypts header in-place. - bool authAndDecryptHeader(h256& io_cipherWithMac); - - /// Authenticates and decrypts frame in-place. - bool authAndDecryptFrame(bytesRef io_cipherWithMac); - - h128 egressDigest(); - - h128 ingressDigest(); - - void updateEgressMACWithHeader(bytesConstRef _headerCipher); - - void updateEgressMACWithEndOfFrame(bytesConstRef _cipher); - - void updateIngressMACWithHeader(bytesConstRef _headerCipher); - - void updateIngressMACWithEndOfFrame(bytesConstRef _cipher); - -private: - void updateMAC(CryptoPP::SHA3_256& _mac, h128 const& _seed = h128()); - - CryptoPP::CTR_Mode::Encryption m_frameEnc; - CryptoPP::CTR_Mode::Encryption m_frameDec; - CryptoPP::ECB_Mode::Encryption m_macEnc; - CryptoPP::SHA3_256 m_egressMac; - CryptoPP::SHA3_256 m_ingressMac; - - bi::tcp::socket* m_socket; -}; - class RLPXHandshake: public std::enable_shared_from_this { -public: friend class RLPXFrameIO; - enum State { Error = -1, @@ -90,17 +48,18 @@ public: ReadHello, StartSession }; - + +public: /// Handshake for ingress connection. Takes ownership of socket. - RLPXHandshake(Host* _host, bi::tcp::socket* _socket): m_host(_host), m_socket(std::move(_socket)), m_originated(false) { crypto::Nonce::get().ref().copyTo(m_nonce.ref()); } + RLPXHandshake(Host* _host, std::shared_ptr const& _socket): m_host(_host), m_originated(false), m_socket(_socket) { crypto::Nonce::get().ref().copyTo(m_nonce.ref()); } /// Handshake for egress connection to _remote. Takes ownership of socket. - RLPXHandshake(Host* _host, bi::tcp::socket* _socket, NodeId _remote): m_host(_host), m_remote(_remote), m_socket(std::move(_socket)), m_originated(true) { crypto::Nonce::get().ref().copyTo(m_nonce.ref()); } - - ~RLPXHandshake() { delete m_socket; } + RLPXHandshake(Host* _host, std::shared_ptr const& _socket, NodeId _remote): m_host(_host), m_remote(_remote), m_originated(true), m_socket(_socket) { crypto::Nonce::get().ref().copyTo(m_nonce.ref()); } + + ~RLPXHandshake() {} void start() { transition(); } - + protected: void writeAuth(); void readAuth(); @@ -118,8 +77,6 @@ protected: /// Node id of remote host for socket. NodeId m_remote; - - bi::tcp::socket* m_socket; bool m_originated = false; /// Buffers for encoded and decoded handshake phases @@ -138,6 +95,7 @@ protected: /// Frame IO is used to read frame for last step of handshake authentication. std::unique_ptr m_io; + std::shared_ptr m_socket; }; } diff --git a/libp2p/Session.cpp b/libp2p/Session.cpp index e11e70e35..f44fa2d6e 100644 --- a/libp2p/Session.cpp +++ b/libp2p/Session.cpp @@ -16,6 +16,7 @@ */ /** @file Session.cpp * @author Gav Wood + * @author Alex Leverington * @date 2014 */ @@ -39,9 +40,8 @@ using namespace dev::p2p; Session::Session(Host* _s, RLPXFrameIO _io, std::shared_ptr const& _n, PeerSessionInfo _info): m_server(_s), -#warning fixme - m_socket(move(*_io.m_socket)), m_io(move(_io)), + m_socket(m_io.socket()), m_peer(_n), m_info(_info), m_ping(chrono::steady_clock::time_point::max()) diff --git a/libp2p/Session.h b/libp2p/Session.h index 5767b7756..89ef5fd74 100644 --- a/libp2p/Session.h +++ b/libp2p/Session.h @@ -16,6 +16,7 @@ */ /** @file Session.h * @author Gav Wood + * @author Alex Leverington * @date 2014 */ @@ -104,8 +105,8 @@ private: Host* m_server; ///< The host that owns us. Never null. - mutable bi::tcp::socket m_socket; ///< Socket for the peer's connection. Mutable to ask for native_handle(). RLPXFrameIO m_io; ///< Transport over which packets are sent. + bi::tcp::socket& m_socket; ///< Socket for the peer's connection. Mutex x_writeQueue; ///< Mutex for the write queue. std::deque m_writeQueue; ///< The write queue. std::array m_data; ///< Buffer for ingress packet data. From df96fcd03bab25d74ca52b0bf533e9646bab2763 Mon Sep 17 00:00:00 2001 From: subtly Date: Fri, 6 Mar 2015 07:08:34 +0100 Subject: [PATCH 031/100] separate packet type. add coding. --- libethereum/EthereumPeer.cpp | 28 ++++---- libp2p/Capability.cpp | 2 +- libp2p/Host.cpp | 7 +- libp2p/RLPxFrameIO.cpp | 65 ++++++++++------- libp2p/RLPxFrameIO.h | 7 +- libp2p/RLPxHandshake.cpp | 10 +-- libp2p/RLPxHandshake.h | 2 +- libp2p/Session.cpp | 136 +++++++++++++++++++---------------- libp2p/Session.h | 11 ++- libwhisper/WhisperPeer.cpp | 2 +- test/peer.cpp | 15 ++-- 11 files changed, 156 insertions(+), 129 deletions(-) diff --git a/libethereum/EthereumPeer.cpp b/libethereum/EthereumPeer.cpp index 95e1aadda..a546c3b22 100644 --- a/libethereum/EthereumPeer.cpp +++ b/libethereum/EthereumPeer.cpp @@ -297,13 +297,13 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r) { case StatusPacket: { - m_protocolVersion = _r[1].toInt(); - m_networkId = _r[2].toInt(); + m_protocolVersion = _r[0].toInt(); + m_networkId = _r[1].toInt(); // a bit dirty as we're misusing these to communicate the values to transition, but harmless. - m_totalDifficulty = _r[3].toInt(); - m_latestHash = _r[4].toHash(); - auto genesisHash = _r[5].toHash(); + m_totalDifficulty = _r[2].toInt(); + m_latestHash = _r[3].toHash(); + auto genesisHash = _r[4].toHash(); clogS(NetMessageSummary) << "Status:" << m_protocolVersion << "/" << m_networkId << "/" << genesisHash.abridged() << ", TD:" << m_totalDifficulty << "=" << m_latestHash.abridged(); @@ -327,7 +327,7 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r) clogS(NetMessageSummary) << "Transactions (" << dec << (_r.itemCount() - 1) << "entries)"; addRating(_r.itemCount() - 1); Guard l(x_knownTransactions); - for (unsigned i = 1; i < _r.itemCount(); ++i) + for (unsigned i = 0; i < _r.itemCount(); ++i) { auto h = sha3(_r[i].data()); m_knownTransactions.insert(h); @@ -339,8 +339,8 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r) } case GetBlockHashesPacket: { - h256 later = _r[1].toHash(); - unsigned limit = _r[2].toInt(); + h256 later = _r[0].toHash(); + unsigned limit = _r[1].toInt(); clogS(NetMessageSummary) << "GetBlockHashes (" << limit << "entries," << later.abridged() << ")"; unsigned c = min(host()->m_chain.number(later), limit); @@ -367,7 +367,7 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r) transition(Asking::Blocks); return true; } - for (unsigned i = 1; i < _r.itemCount(); ++i) + for (unsigned i = 0; i < _r.itemCount(); ++i) { auto h = _r[i].toHash(); if (host()->m_chain.isKnown(h)) @@ -388,7 +388,7 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r) // return the requested blocks. bytes rlp; unsigned n = 0; - for (unsigned i = 1; i < _r.itemCount() && i <= c_maxBlocks; ++i) + for (unsigned i = 0; i < _r.itemCount() && i <= c_maxBlocks; ++i) { auto b = host()->m_chain.block(_r[i].toHash()); if (b.size()) @@ -422,7 +422,7 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r) unsigned got = 0; unsigned repeated = 0; - for (unsigned i = 1; i < _r.itemCount(); ++i) + for (unsigned i = 0; i < _r.itemCount(); ++i) { auto h = BlockInfo::headerHash(_r[i].data()); if (m_sub.noteBlock(h)) @@ -467,14 +467,14 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r) } case NewBlockPacket: { - auto h = BlockInfo::headerHash(_r[1].data()); + auto h = BlockInfo::headerHash(_r[0].data()); clogS(NetMessageSummary) << "NewBlock: " << h.abridged(); if (_r.itemCount() != 3) disable("NewBlock without 2 data fields."); else { - switch (host()->m_bq.import(_r[1].data(), host()->m_chain)) + switch (host()->m_bq.import(_r[0].data(), host()->m_chain)) { case ImportResult::Success: addRating(100); @@ -493,7 +493,7 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r) case ImportResult::UnknownParent: clogS(NetMessageSummary) << "Received block with no known parent. Resyncing..."; - setNeedsSyncing(h, _r[2].toInt()); + setNeedsSyncing(h, _r[1].toInt()); break; } Guard l(x_knownBlocks); diff --git a/libp2p/Capability.cpp b/libp2p/Capability.cpp index 60f4fd7f3..dccc130cd 100644 --- a/libp2p/Capability.cpp +++ b/libp2p/Capability.cpp @@ -45,7 +45,7 @@ void Capability::disable(std::string const& _problem) RLPStream& Capability::prep(RLPStream& _s, unsigned _id, unsigned _args) { - return Session::prep(_s).appendList(_args + 1).append(_id + m_idOffset); + return _s.appendRaw(bytes(1, _id + m_idOffset)).appendList(_args); } void Capability::sealAndSend(RLPStream& _s) diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index 057453d8f..eb5e8adc1 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -187,7 +187,7 @@ bool Host::startPeerSession(Public const& _id, RLP const& _rlp, RLPXFrameIO* _io clog(NetMessageSummary) << "Hello: " << clientVersion << "V[" << protocolVersion << "]" << _id.abridged() << showbase << capslog.str() << dec << listenPort; // create session so disconnects are managed - auto ps = make_shared(this, move(*_io), p, PeerSessionInfo({_id, clientVersion, _endpoint.address().to_string(), listenPort, chrono::steady_clock::duration(), _rlp[3].toSet(), 0, map()})); + auto ps = make_shared(this, _io, p, PeerSessionInfo({_id, clientVersion, _endpoint.address().to_string(), listenPort, chrono::steady_clock::duration(), _rlp[3].toSet(), 0, map()})); if (protocolVersion != this->protocolVersion()) { ps->disconnect(IncompatibleProtocol); @@ -273,11 +273,6 @@ void Host::onNodeTableEvent(NodeId const& _n, NodeTableEventType const& _e) void Host::seal(bytes& _b) { - uint32_t len = (uint32_t)_b.size() - 4; - _b[0] = (len >> 24) & 0xff; - _b[1] = (len >> 16) & 0xff; - _b[2] = (len >> 8) & 0xff; - _b[3] = len & 0xff; } void Host::determinePublic(string const& _publicAddress, bool _upnp) diff --git a/libp2p/RLPxFrameIO.cpp b/libp2p/RLPxFrameIO.cpp index 3ddaf9143..8531759e9 100644 --- a/libp2p/RLPxFrameIO.cpp +++ b/libp2p/RLPxFrameIO.cpp @@ -57,12 +57,16 @@ RLPXFrameIO::RLPXFrameIO(RLPXHandshake const& _init): m_socket(_init.m_socket) // aes-secret = sha3(ecdhe-shared-secret || shared-secret) sha3(keyMaterial, outRef); // output aes-secret - m_frameEnc.SetKeyWithIV(outRef.data(), h128::size, h128().data()); - m_frameDec.SetKeyWithIV(outRef.data(), h128::size, h128().data()); + SecByteBlock aesSecretEnc(outRef.data(), h128::size); + SecByteBlock aesSecretDec(outRef.data(), h128::size); + SecByteBlock emptyIV(h128::size); + m_frameEnc.SetKeyWithIV(aesSecretEnc, h128::size, emptyIV); + m_frameDec.SetKeyWithIV(aesSecretDec, h128::size, emptyIV); // mac-secret = sha3(ecdhe-shared-secret || aes-secret) sha3(keyMaterial, outRef); // output mac-secret - m_macEnc.SetKey(outRef.data(), h128::size); + SecByteBlock macSecret(outRef.data(), h128::size); + m_macEnc.SetKey(macSecret, h128::size); // Initiator egress-mac: sha3(mac-secret^recipient-nonce || auth-sent-init) // ingress-mac: sha3(mac-secret^initiator-nonce || auth-recvd-ack) @@ -91,16 +95,17 @@ void RLPXFrameIO::writeSingleFramePacket(bytesConstRef _packet, bytes& o_bytes) // current/old packet format: prep(_s).appendList(_args + 1).append((unsigned)_id); RLPStream header; - header.appendRaw(bytes({byte(_packet.size() >> 16), byte(_packet.size() >> 8), byte(_packet.size())})); + uint32_t len = (uint32_t)_packet.size(); + header.appendRaw(bytes({byte((len >> 16) & 0xff), byte((len >> 8) & 0xff), byte(len & 0xff)})); // zeroHeader: []byte{0xC2, 0x80, 0x80}. Should be rlpList(protocolType,seqId,totalPacketSize). header.appendRaw(bytes({0xc2,0x80,0x80})); // TODO: SECURITY check that header is <= 16 bytes - - bytes headerWithMac; - header.swapOut(headerWithMac); - headerWithMac.resize(32); - m_frameEnc.ProcessData(headerWithMac.data(), headerWithMac.data(), 16); + + bytes headerWithMac(32); + bytes headerBytes(16); + bytesConstRef(&header.out()).copyTo(&headerBytes); + m_frameEnc.ProcessData(headerWithMac.data(), headerBytes.data(), 16); updateEgressMACWithHeader(bytesConstRef(&headerWithMac).cropped(0, 16)); egressDigest().ref().copyTo(bytesRef(&headerWithMac).cropped(h128::size,h128::size)); @@ -116,17 +121,16 @@ void RLPXFrameIO::writeSingleFramePacket(bytesConstRef _packet, bytes& o_bytes) updateEgressMACWithEndOfFrame(packetWithPaddingRef); bytesRef macRef(o_bytes.data() + 32 + _packet.size() + padding, h128::size); egressDigest().ref().copyTo(macRef); - clog(NetConnect) << "SENT FRAME " << _packet.size() << *(h128*)macRef.data(); - clog(NetConnect) << "FRAME TAIL " << *(h128*)(o_bytes.data() + 32 + _packet.size() + padding); } -bool RLPXFrameIO::authAndDecryptHeader(h256& io) +bool RLPXFrameIO::authAndDecryptHeader(bytesRef io) { - updateIngressMACWithHeader(io.ref()); - bytesConstRef macRef = io.ref().cropped(h128::size, h128::size); + asserts(io.size() == h256::size); + updateIngressMACWithHeader(io); + bytesConstRef macRef = io.cropped(h128::size, h128::size); if (*(h128*)macRef.data() != ingressDigest()) return false; - m_frameDec.ProcessData(io.data(), io.data(), 16); + m_frameDec.ProcessData(io.data(), io.data(), h128::size); return true; } @@ -159,7 +163,7 @@ h128 RLPXFrameIO::ingressDigest() void RLPXFrameIO::updateEgressMACWithHeader(bytesConstRef _headerCipher) { - updateMAC(m_egressMac, *(h128*)_headerCipher.data()); + updateMAC(m_egressMac, _headerCipher.cropped(0, 16)); } void RLPXFrameIO::updateEgressMACWithEndOfFrame(bytesConstRef _cipher) @@ -170,13 +174,12 @@ void RLPXFrameIO::updateEgressMACWithEndOfFrame(bytesConstRef _cipher) SHA3_256 prev(m_egressMac); h128 digest; prev.TruncatedFinal(digest.data(), h128::size); - clog(NetConnect) << "EGRESS FRAMEMAC " << _cipher.size() << digest; } } void RLPXFrameIO::updateIngressMACWithHeader(bytesConstRef _headerCipher) { - updateMAC(m_ingressMac, *(h128*)_headerCipher.data()); + updateMAC(m_ingressMac, _headerCipher.cropped(0, 16)); } void RLPXFrameIO::updateIngressMACWithEndOfFrame(bytesConstRef _cipher) @@ -187,20 +190,28 @@ void RLPXFrameIO::updateIngressMACWithEndOfFrame(bytesConstRef _cipher) SHA3_256 prev(m_ingressMac); h128 digest; prev.TruncatedFinal(digest.data(), h128::size); - clog(NetConnect) << "INGRESS FRAMEMAC " << _cipher.size() << digest; } } -void RLPXFrameIO::updateMAC(SHA3_256& _mac, h128 const& _seed) +void RLPXFrameIO::updateMAC(SHA3_256& _mac, bytesConstRef _seed) { + if (_seed.size() && _seed.size() != h128::size) + asserts(false); + SHA3_256 prevDigest(_mac); - h128 prevDigestOut; - prevDigest.TruncatedFinal(prevDigestOut.data(), h128::size); - - h128 encDigest; - m_macEnc.ProcessData(encDigest.data(), prevDigestOut.data(), h128::size); - encDigest ^= (!!_seed ? _seed : prevDigestOut); - + h128 encDigest(h128::size); + prevDigest.TruncatedFinal(encDigest.data(), h128::size); + h128 prevDigestOut = encDigest; + + { + Guard l(x_macEnc); + m_macEnc.ProcessData(encDigest.data(), encDigest.data(), 16); + } + if (_seed.size()) + encDigest ^= *(h128*)_seed.data(); + else + encDigest ^= *(h128*)prevDigestOut.data(); + // update mac for final digest _mac.Update(encDigest.data(), h128::size); } diff --git a/libp2p/RLPxFrameIO.h b/libp2p/RLPxFrameIO.h index 07211d4de..a8b2011eb 100644 --- a/libp2p/RLPxFrameIO.h +++ b/libp2p/RLPxFrameIO.h @@ -26,6 +26,7 @@ #include #include #include +#include #include "Common.h" namespace ba = boost::asio; namespace bi = boost::asio::ip; @@ -57,11 +58,12 @@ class RLPXFrameIO friend class Session; public: RLPXFrameIO(RLPXHandshake const& _init); + ~RLPXFrameIO() {} void writeSingleFramePacket(bytesConstRef _packet, bytes& o_bytes); /// Authenticates and decrypts header in-place. - bool authAndDecryptHeader(h256& io_cipherWithMac); + bool authAndDecryptHeader(bytesRef io_cipherWithMac); /// Authenticates and decrypts frame in-place. bool authAndDecryptFrame(bytesRef io_cipherWithMac); @@ -82,10 +84,11 @@ protected: bi::tcp::socket& socket() { return m_socket->ref(); } private: - void updateMAC(CryptoPP::SHA3_256& _mac, h128 const& _seed = h128()); + void updateMAC(CryptoPP::SHA3_256& _mac, bytesConstRef _seed = bytesConstRef()); CryptoPP::CTR_Mode::Encryption m_frameEnc; CryptoPP::CTR_Mode::Encryption m_frameDec; + Mutex x_macEnc; CryptoPP::ECB_Mode::Encryption m_macEnc; CryptoPP::SHA3_256 m_egressMac; CryptoPP::SHA3_256 m_ingressMac; diff --git a/libp2p/RLPxHandshake.cpp b/libp2p/RLPxHandshake.cpp index bca1afd61..004880c39 100644 --- a/libp2p/RLPxHandshake.cpp +++ b/libp2p/RLPxHandshake.cpp @@ -166,7 +166,7 @@ void RLPXHandshake::transition(boost::system::error_code _ech) else clog(NetConnect) << "p2p.connect.ingress sending capabilities handshake"; - m_io.reset(new RLPXFrameIO(*this)); + m_io = new RLPXFrameIO(*this); // old packet format // 5 arguments, HelloPacket @@ -193,14 +193,14 @@ void RLPXHandshake::transition(boost::system::error_code _ech) // read frame header m_handshakeInBuffer.resize(h256::size); - ba::async_read(m_socket->ref(), boost::asio::buffer(m_handshakeInBuffer, h256::size), [this,self](boost::system::error_code ec, std::size_t length) + ba::async_read(m_socket->ref(), boost::asio::buffer(m_handshakeInBuffer, h256::size), [this, self](boost::system::error_code ec, std::size_t length) { if (ec) transition(ec); else { /// authenticate and decrypt header - if (!m_io->authAndDecryptHeader(*(h256*)m_handshakeInBuffer.data())) + if (!m_io->authAndDecryptHeader(bytesRef(m_handshakeInBuffer.data(), h256::size))) { m_nextState = Error; transition(); @@ -215,7 +215,7 @@ void RLPXHandshake::transition(boost::system::error_code _ech) if (frameSize > 1024) { // all future frames: 16777216 - clog(NetWarn) << (m_originated ? "p2p.connect.egress" : "p2p.connect.ingress") << "hello frame is too large"; + clog(NetWarn) << (m_originated ? "p2p.connect.egress" : "p2p.connect.ingress") << "hello frame is too large" << frameSize; m_nextState = Error; transition(); return; @@ -251,7 +251,7 @@ void RLPXHandshake::transition(boost::system::error_code _ech) return; } - m_host->startPeerSession(m_remote, rlp, m_io.get(), m_socket->remoteEndpoint()); + m_host->startPeerSession(m_remote, rlp, m_io, m_socket->remoteEndpoint()); } }); } diff --git a/libp2p/RLPxHandshake.h b/libp2p/RLPxHandshake.h index 12abe4379..cd5e7fb83 100644 --- a/libp2p/RLPxHandshake.h +++ b/libp2p/RLPxHandshake.h @@ -94,7 +94,7 @@ protected: h256 m_remoteNonce; /// Frame IO is used to read frame for last step of handshake authentication. - std::unique_ptr m_io; + RLPXFrameIO* m_io; std::shared_ptr m_socket; }; diff --git a/libp2p/Session.cpp b/libp2p/Session.cpp index f44fa2d6e..da83c0b5e 100644 --- a/libp2p/Session.cpp +++ b/libp2p/Session.cpp @@ -38,10 +38,10 @@ using namespace dev::p2p; #endif #define clogS(X) dev::LogOutputStream(false) << "| " << std::setw(2) << m_socket.native_handle() << "] " -Session::Session(Host* _s, RLPXFrameIO _io, std::shared_ptr const& _n, PeerSessionInfo _info): +Session::Session(Host* _s, RLPXFrameIO* _io, std::shared_ptr const& _n, PeerSessionInfo _info): m_server(_s), - m_io(move(_io)), - m_socket(m_io.socket()), + m_io(_io), + m_socket(m_io->socket()), m_peer(_n), m_info(_info), m_ping(chrono::steady_clock::time_point::max()) @@ -67,6 +67,7 @@ Session::~Session() } } catch (...){} + delete m_io; } NodeId Session::id() const @@ -144,21 +145,21 @@ void Session::serviceNodesRequest() addNote("peers", "done"); } -bool Session::interpret(RLP const& _r) +bool Session::interpret(PacketType _t, RLP const& _r) { m_lastReceived = chrono::steady_clock::now(); - clogS(NetRight) << _r; + clogS(NetRight) << _t << _r; try // Generic try-catch block designed to capture RLP format errors - TODO: give decent diagnostics, make a bit more specific over what is caught. { - switch ((PacketType)_r[0].toInt()) + switch (_t) { case DisconnectPacket: { string reason = "Unspecified"; - auto r = (DisconnectReason)_r[1].toInt(); - if (!_r[1].isInt()) + auto r = (DisconnectReason)_r[0].toInt(); + if (!_r[0].isInt()) drop(BadProtocol); else { @@ -197,7 +198,7 @@ bool Session::interpret(RLP const& _r) clogS(NetTriviaSummary) << "Peers (" << dec << (_r.itemCount() - 1) << " entries)"; m_weRequestedNodes = false; - for (unsigned i = 1; i < _r.itemCount(); ++i) + for (unsigned i = 0; i < _r.itemCount(); ++i) { bi::address peerAddress; if (_r[i][0].size() == 16) @@ -247,12 +248,11 @@ bool Session::interpret(RLP const& _r) break; default: { - auto id = _r[0].toInt(); for (auto const& i: m_capabilities) - if (id >= i.second->m_idOffset && id - i.second->m_idOffset < i.second->hostCapability()->messageCount()) + if (_t >= i.second->m_idOffset && _t - i.second->m_idOffset < i.second->hostCapability()->messageCount()) { if (i.second->m_enabled) - return i.second->interpret(id - i.second->m_idOffset, _r); + return i.second->interpret(_t - i.second->m_idOffset, _r); else return true; } @@ -278,12 +278,7 @@ void Session::ping() RLPStream& Session::prep(RLPStream& _s, PacketType _id, unsigned _args) { - return prep(_s).appendList(_args + 1).append((unsigned)_id); -} - -RLPStream& Session::prep(RLPStream& _s) -{ - return _s.appendRaw(bytes(4, 0)); + return _s.appendRaw(bytes(1, _id)).appendList(_args); } void Session::sealAndSend(RLPStream& _s) @@ -296,32 +291,32 @@ void Session::sealAndSend(RLPStream& _s) bool Session::checkPacket(bytesConstRef _msg) { - if (_msg.size() < 5) + if (_msg.size() < 2) return false; - uint32_t len = ((_msg[0] * 256 + _msg[1]) * 256 + _msg[2]) * 256 + _msg[3]; - if (_msg.size() != len + 4) + if (_msg[0] > 0x7f) return false; - RLP r(_msg.cropped(4)); - if (r.actualSize() != len) + RLP r(_msg.cropped(1)); + if (r.actualSize() + 1 != _msg.size()) return false; return true; } -void Session::send(bytesConstRef _msg) -{ - send(_msg.toBytes()); -} +//void Session::send(bytesConstRef _msg) +//{ +// send(_msg.toBytes()); +//} void Session::send(bytes&& _msg) { - clogS(NetLeft) << RLP(bytesConstRef(&_msg).cropped(4)); + clogS(NetLeft) << RLP(bytesConstRef(&_msg).cropped(1)); - if (!checkPacket(bytesConstRef(&_msg))) + bytesConstRef msg(&_msg); + if (!checkPacket(msg)) clogS(NetWarn) << "INVALID PACKET CONSTRUCTED!"; if (!m_socket.is_open()) return; - + bool doWrite = false; { Guard l(x_writeQueue); @@ -336,6 +331,7 @@ void Session::send(bytes&& _msg) void Session::write() { const bytes& bytes = m_writeQueue[0]; + m_io->writeSingleFramePacket(&bytes, m_writeQueue[0]); auto self(shared_from_this()); ba::async_write(m_socket, ba::buffer(bytes), [this, self](boost::system::error_code ec, std::size_t /*length*/) { @@ -415,7 +411,7 @@ void Session::doRead() return; auto self(shared_from_this()); - m_socket.async_read_some(boost::asio::buffer(m_data), [this,self](boost::system::error_code ec, std::size_t length) + ba::async_read(m_socket, boost::asio::buffer(m_data, h256::size), [this,self](boost::system::error_code ec, std::size_t length) { if (ec && ec.category() != boost::asio::error::get_misc_category() && ec.value() != boost::asio::error::eof) { @@ -426,50 +422,66 @@ void Session::doRead() return; else { - try + /// authenticate and decrypt header + bytesRef header(m_data.data(), h256::size); + if (!m_io->authAndDecryptHeader(header)) + { + clog(NetWarn) << "header decrypt failed"; + drop(BadProtocol); // todo: better error + return; + } + + /// check frame size + uint32_t frameSize = (m_data[0] * 256 + m_data[1]) * 256 + m_data[2]; + if (frameSize > 16777216) + { + clog(NetWarn) << "frame size too large"; + drop(BadProtocol); + return; + } + + /// rlp of header has protocol-type, sequence-id[, total-packet-size] + bytes headerRLP(13); + bytesConstRef(m_data.data(), h128::size).cropped(3).copyTo(&headerRLP); + + /// read padded frame and mac + auto tlen = frameSize + ((16 - (frameSize % 16)) % 16) + h128::size; + ba::async_read(m_socket, boost::asio::buffer(m_data, tlen), [this, self, headerRLP, frameSize, tlen](boost::system::error_code ec, std::size_t length) { - m_incoming.resize(m_incoming.size() + length); - memcpy(m_incoming.data() + m_incoming.size() - length, m_data.data(), length); - - // 4 bytes for length header - const uint32_t c_hLen = 4; - while (m_incoming.size() > c_hLen) + if (ec && ec.category() != boost::asio::error::get_misc_category() && ec.value() != boost::asio::error::eof) + { + clogS(NetWarn) << "Error reading: " << ec.message(); + drop(TCPError); + } + else if (ec && length == 0) + return; + else { - // break if data recvd is less than expected size of packet. - uint32_t len = fromBigEndian(bytesConstRef(m_incoming.data(), c_hLen)); - uint32_t tlen = c_hLen + len; - if (m_incoming.size() < tlen) - break; - bytesConstRef frame(m_incoming.data(), tlen); - bytesConstRef packet = frame.cropped(c_hLen); + if (!m_io->authAndDecryptFrame(bytesRef(m_data.data(), tlen))) + { + clog(NetWarn) << "frame decrypt failed"; + drop(BadProtocol); // todo: better error + return; + } + + bytesConstRef frame(m_data.data(), frameSize); if (!checkPacket(frame)) { - cerr << "Received " << packet.size() << ": " << toHex(packet) << endl; + cerr << "Received " << frame.size() << ": " << toHex(frame) << endl; clogS(NetWarn) << "INVALID MESSAGE RECEIVED"; disconnect(BadProtocol); return; } else { - RLP r(packet); - if (!interpret(r)) + auto packetType = (PacketType)RLP(frame.cropped(0, 1)).toInt(); + RLP r(frame.cropped(1)); + if (!interpret(packetType, r)) clogS(NetWarn) << "Couldn't interpret packet." << RLP(r); } - memmove(m_incoming.data(), m_incoming.data() + tlen, m_incoming.size() - tlen); - m_incoming.resize(m_incoming.size() - tlen); + doRead(); } - doRead(); - } - catch (Exception const& _e) - { - clogS(NetWarn) << "ERROR: " << diagnostic_information(_e); - drop(BadProtocol); - } - catch (std::exception const& _e) - { - clogS(NetWarn) << "ERROR: " << _e.what(); - drop(BadProtocol); - } + }); } }); } diff --git a/libp2p/Session.h b/libp2p/Session.h index 89ef5fd74..77b7de7bb 100644 --- a/libp2p/Session.h +++ b/libp2p/Session.h @@ -54,7 +54,7 @@ class Session: public std::enable_shared_from_this friend class HostCapabilityFace; public: - Session(Host* _server, RLPXFrameIO _io, std::shared_ptr const& _n, PeerSessionInfo _info); + Session(Host* _server, RLPXFrameIO* _io, std::shared_ptr const& _n, PeerSessionInfo _info); virtual ~Session(); void start(); @@ -71,7 +71,6 @@ public: std::shared_ptr cap() const { try { return std::static_pointer_cast(m_capabilities.at(std::make_pair(PeerCap::name(), PeerCap::version()))); } catch (...) { return nullptr; } } static RLPStream& prep(RLPStream& _s, PacketType _t, unsigned _args = 0); - static RLPStream& prep(RLPStream& _s); void sealAndSend(RLPStream& _s); int rating() const; @@ -86,7 +85,7 @@ public: private: void send(bytes&& _msg); - void send(bytesConstRef _msg); +// void send(bytesConstRef _msg); /// Drop the connection for the reason @a _r. void drop(DisconnectReason _r); @@ -98,18 +97,18 @@ private: void write(); /// Interpret an incoming message. - bool interpret(RLP const& _r); + bool interpret(PacketType _t, RLP const& _r); /// @returns true iff the _msg forms a valid message for sending or receiving on the network. static bool checkPacket(bytesConstRef _msg); Host* m_server; ///< The host that owns us. Never null. - RLPXFrameIO m_io; ///< Transport over which packets are sent. + RLPXFrameIO* m_io; ///< Transport over which packets are sent. bi::tcp::socket& m_socket; ///< Socket for the peer's connection. Mutex x_writeQueue; ///< Mutex for the write queue. std::deque m_writeQueue; ///< The write queue. - std::array m_data; ///< Buffer for ingress packet data. + std::array m_data; ///< Buffer for ingress packet data. bytes m_incoming; ///< Read buffer for ingress bytes. unsigned m_protocolVersion = 0; ///< The protocol version of the peer. diff --git a/libwhisper/WhisperPeer.cpp b/libwhisper/WhisperPeer.cpp index 7480a104e..53ea91a9e 100644 --- a/libwhisper/WhisperPeer.cpp +++ b/libwhisper/WhisperPeer.cpp @@ -55,7 +55,7 @@ bool WhisperPeer::interpret(unsigned _id, RLP const& _r) { case StatusPacket: { - auto protocolVersion = _r[1].toInt(); + auto protocolVersion = _r[0].toInt(); clogS(NetMessageSummary) << "Status: " << protocolVersion; diff --git a/test/peer.cpp b/test/peer.cpp index 7f3c19e1e..0fe3fd1ed 100644 --- a/test/peer.cpp +++ b/test/peer.cpp @@ -32,6 +32,9 @@ BOOST_AUTO_TEST_SUITE(p2p) BOOST_AUTO_TEST_CASE(host) { + auto oldLogVerbosity = g_logVerbosity; + g_logVerbosity = 10; + NetworkPreferences host1prefs(30301, "127.0.0.1", true, true); NetworkPreferences host2prefs(30302, "127.0.0.1", true, true); @@ -44,10 +47,14 @@ BOOST_AUTO_TEST_CASE(host) host1.addNode(node2, "127.0.0.1", host2prefs.listenPort, host2prefs.listenPort); - this_thread::sleep_for(chrono::seconds(1)); + this_thread::sleep_for(chrono::seconds(3)); + + auto host1peerCount = host1.peerCount(); + auto host2peerCount = host2.peerCount(); + BOOST_REQUIRE_EQUAL(host1peerCount, 1); + BOOST_REQUIRE_EQUAL(host2peerCount, 1); - BOOST_REQUIRE_EQUAL(host1.peerCount(), 1); - BOOST_REQUIRE_EQUAL(host2.peerCount(), host1.peerCount()); + g_logVerbosity = oldLogVerbosity; } BOOST_AUTO_TEST_CASE(save_nodes) @@ -71,7 +78,7 @@ BOOST_AUTO_TEST_CASE(save_nodes) for (auto const& h: hosts) host2.addNode(h->id(), "127.0.0.1", h->listenPort(), h->listenPort()); - this_thread::sleep_for(chrono::milliseconds(1000)); + this_thread::sleep_for(chrono::milliseconds(2000)); bytes firstHostNetwork(host.saveNetwork()); bytes secondHostNetwork(host.saveNetwork()); From 1092518fe7543cd9fcf57bc4d7806acd6fa673b2 Mon Sep 17 00:00:00 2001 From: yann300 Date: Fri, 6 Mar 2015 11:45:57 +0100 Subject: [PATCH 032/100] - logs pane below the status pane - retrieve javascript message --- mix/AppContext.cpp | 4 + mix/CodeEditorExtensionManager.cpp | 6 +- mix/qml/LogsPane.qml | 143 ++++++++++++++++++++++++ mix/qml/MainContent.qml | 14 +-- mix/qml/StatusPane.qml | 93 +++++++++++++--- mix/qml/WebPreview.qml | 11 +- mix/qml/img/broom.png | Bin 0 -> 501 bytes mix/qml/img/copy.png | Bin 0 -> 342 bytes mix/res.qrc | 4 + mix/sortfilterproxymodel.cpp | 170 +++++++++++++++++++++++++++++ mix/sortfilterproxymodel.h | 107 ++++++++++++++++++ 11 files changed, 522 insertions(+), 30 deletions(-) create mode 100644 mix/qml/LogsPane.qml create mode 100644 mix/qml/img/broom.png create mode 100644 mix/qml/img/copy.png create mode 100644 mix/sortfilterproxymodel.cpp create mode 100644 mix/sortfilterproxymodel.h diff --git a/mix/AppContext.cpp b/mix/AppContext.cpp index 29124a39a..46bb0f98d 100644 --- a/mix/AppContext.cpp +++ b/mix/AppContext.cpp @@ -22,6 +22,7 @@ * - KeyEventManager */ +#include #include #include #include @@ -37,6 +38,7 @@ #include "QVariableDefinition.h" #include "HttpServer.h" #include "AppContext.h" +#include "sortfilterproxymodel.h" using namespace dev; using namespace dev::eth; @@ -74,6 +76,7 @@ void AppContext::load() qmlRegisterType("org.ethereum.qml.QBoolType", 1, 0, "QBoolType"); qmlRegisterType("org.ethereum.qml.QVariableDeclaration", 1, 0, "QVariableDeclaration"); qmlRegisterType("org.ethereum.qml.RecordLogEntry", 1, 0, "RecordLogEntry"); + qmlRegisterType("org.ethereum.qml.SortFilterProxyModel", 1, 0, "SortFilterProxyModel"); QQmlComponent projectModelComponent(m_applicationEngine, QUrl("qrc:/qml/ProjectModel.qml")); QObject* projectModel = projectModelComponent.create(); if (projectModelComponent.isError()) @@ -86,6 +89,7 @@ void AppContext::load() m_applicationEngine->rootContext()->setContextProperty("projectModel", projectModel); qmlRegisterType("CodeEditorExtensionManager", 1, 0, "CodeEditorExtensionManager"); qmlRegisterType("HttpServer", 1, 0, "HttpServer"); + m_applicationEngine->load(QUrl("qrc:/qml/main.qml")); QWindow *window = qobject_cast(m_applicationEngine->rootObjects().at(0)); window->setIcon(QIcon(":/res/mix_256x256x32.png")); diff --git a/mix/CodeEditorExtensionManager.cpp b/mix/CodeEditorExtensionManager.cpp index 97b808eb2..516184cff 100644 --- a/mix/CodeEditorExtensionManager.cpp +++ b/mix/CodeEditorExtensionManager.cpp @@ -53,10 +53,10 @@ void CodeEditorExtensionManager::loadEditor(QQuickItem* _editor) void CodeEditorExtensionManager::initExtensions() { - std::shared_ptr output = std::make_shared(m_appContext); - QObject::connect(m_appContext->codeModel(), &CodeModel::compilationComplete, this, &CodeEditorExtensionManager::applyCodeHighlight); + //std::shared_ptr output = std::make_shared(m_appContext); + //QObject::connect(m_appContext->codeModel(), &CodeModel::compilationComplete, this, &CodeEditorExtensionManager::applyCodeHighlight); - initExtension(output); + //initExtension(output); } void CodeEditorExtensionManager::initExtension(std::shared_ptr _ext) diff --git a/mix/qml/LogsPane.qml b/mix/qml/LogsPane.qml new file mode 100644 index 000000000..63d940eb6 --- /dev/null +++ b/mix/qml/LogsPane.qml @@ -0,0 +1,143 @@ +import QtQuick 2.0 +import QtQuick.Layouts 1.0 +import QtQuick.Controls 1.1 +import org.ethereum.qml.SortFilterProxyModel 1.0 +import "." + +Rectangle +{ + function push(_level, _type, _content) + { + _content = _content.replace(/\n/g, " ") + logsModel.insert(0, { "type": _type, "date": Qt.formatDateTime(new Date(), "hh:mm:ss dd.MM.yyyy"), "content": _content, "level": _level }); + } + anchors.fill: parent + radius: 5 + color: "#f7f7f7" + border.color: "#808080" + border.width: 2 + ColumnLayout { + height: parent.height - 4 + width: parent.width - 2 + spacing: 0 + Row + { + id: rowAction + Layout.preferredHeight: 35 + height: 35 + anchors.leftMargin: 10 + anchors.left: parent.left + spacing: 5 + Button + { + height: 30 + anchors.verticalCenter: parent.verticalCenter + action: clearAction + iconSource: "qrc:/qml/img/broom.png" + } + + Action { + id: clearAction + enabled: logsModel.count > 0 + tooltip: qsTr("Clear") + onTriggered: { + logsModel.clear() + } + } + + Button + { + height: 30 + anchors.verticalCenter: parent.verticalCenter + action: copytoClipBoardAction + iconSource: "qrc:/qml/img/copy.png" + } + + Action { + id: copytoClipBoardAction + enabled: logsModel.count > 0 + tooltip: qsTr("Copy to Clipboard") + onTriggered: { + var content = ""; + for (var k = 0; k < logsModel.count; k++) + { + var log = logsModel.get(k); + content += log.type + "\t" + log.level + "\t" + log.date + "\t" + log.content; + } + appContext.toClipboard(content); + } + } + } + + ListModel { + id: logsModel + } + + TableView { + id: logsTable + clip: true + Layout.fillWidth: true + Layout.preferredHeight: parent.height - rowAction.height + headerVisible : false + onDoubleClicked: + { + var log = logsModel.get((logsTable.currentRow)); + appContext.toClipboard(log.type + " " + log.level + " " + log.date + " " + log.content); + } + + model: SortFilterProxyModel { + id: proxyModel + source: logsModel + + filterRole: "" + filterString: "" + filterSyntax: SortFilterProxyModel.Wildcard + filterCaseSensitivity: Qt.CaseInsensitive + } + TableViewColumn + { + role: "date" + title: qsTr("date") + width: 150 + delegate: itemDelegate + } + TableViewColumn + { + role: "type" + title: qsTr("type") + width: 100 + delegate: itemDelegate + } + TableViewColumn + { + role: "content" + title: qsTr("content") + width: 700 + delegate: itemDelegate + } + } + + Component { + id: itemDelegate + DefaultLabel { + text: styleData.value; + font.family: "sans serif" + font.pointSize: Style.absoluteSize(-1) + color: { + if (styleData.row > -1) + { + var l = logsModel.get(styleData.row).level + if (l === "error") + return "red" + else if (l === "warning") + return "orange" + else if (l === "info") + return "#808080" + } + else + return "#808080" + } + } + } + } +} diff --git a/mix/qml/MainContent.qml b/mix/qml/MainContent.qml index 081e0cd95..d295e9da0 100644 --- a/mix/qml/MainContent.qml +++ b/mix/qml/MainContent.qml @@ -102,7 +102,7 @@ Rectangle { } CodeEditorExtensionManager { - headerView: headerPaneTabs; + //headerView: headerPaneTabs; } Settings { @@ -133,16 +133,10 @@ Rectangle { } id: headerPaneContainer anchors.fill: parent - TabView { - id: headerPaneTabs - tabsVisible: false - antialiasing: true + StatusPane + { anchors.fill: parent - style: TabViewStyle { - frameOverlap: 1 - tab: Rectangle {} - frame: Rectangle { color: "transparent" } - } + webPreview: webPreview } } } diff --git a/mix/qml/StatusPane.qml b/mix/qml/StatusPane.qml index e526d65bd..87bfb39d5 100644 --- a/mix/qml/StatusPane.qml +++ b/mix/qml/StatusPane.qml @@ -8,6 +8,7 @@ import "." Rectangle { id: statusHeader objectName: "statusPane" + property variant webPreview function updateStatus(message) { @@ -15,7 +16,6 @@ Rectangle { { status.state = ""; status.text = qsTr("Compile successfully."); - logslink.visible = false; debugImg.state = "active"; } else @@ -23,39 +23,52 @@ Rectangle { status.state = "error"; var errorInfo = ErrorLocationFormater.extractErrorInfo(message, true); status.text = errorInfo.errorLocation + " " + errorInfo.errorDetail; - logslink.visible = true; debugImg.state = ""; } debugRunActionIcon.enabled = codeModel.hasContract; } - function infoMessage(text) + function infoMessage(text, type) { status.state = ""; status.text = text - logslink.visible = false; + logPane.push("info",type, text); } - function errorMessage(text) + function warningMessage(text, type) + { + status.state = ""; + status.text = text + logPane.push("warning", type, text); + } + + function errorMessage(text, type) { status.state = "error"; status.text = text - logslink.visible = false; + logPane.push("error", type, text); + } + + Connections { + target: webPreview + onJavaScriptErrorMessage: errorMessage(_content, "javascript") + onJavaScriptWarningMessage: warningMessage(_content, "javascript") + onJavaScriptInfoMessage: infoMessage(_content, "javascript") } Connections { target:clientModel - onRunStarted: infoMessage(qsTr("Running transactions...")); - onRunFailed: errorMessage(qsTr("Error running transactions: " + _message)); - onRunComplete: infoMessage(qsTr("Run complete")); - onNewBlock: infoMessage(qsTr("New block created")); + onRunStarted: infoMessage(qsTr("Running transactions..."), "run"); + onRunFailed: errorMessage(qsTr("Error running transactions: " + _message), "run"); + onRunComplete: infoMessage(qsTr("Run complete"), "run"); + onNewBlock: infoMessage(qsTr("New block created"), "state"); } Connections { target:projectModel - onDeploymentStarted: infoMessage(qsTr("Running deployment...")); - onDeploymentError: errorMessage(error); - onDeploymentComplete: infoMessage(qsTr("Deployment complete")); - onDeploymentStepChanged: infoMessage(message); + onDeploymentStarted: infoMessage(qsTr("Running deployment..."), "deployment"); + onDeploymentError: errorMessage(error, "deployment"); + onDeploymentComplete: infoMessage(qsTr("Deployment complete"), "deployment"); + onDeploymentStepChanged: infoMessage(message, "deployment"); } Connections { target: codeModel @@ -133,6 +146,53 @@ Rectangle { id: toolTipInfo tooltip: "" } + + Rectangle + { + function toggle() + { + if (logsContainer.state === "opened") + logsContainer.state = "closed" + else + logsContainer.state = "opened"; + } + + id: logsContainer + width: 1000 + height: 0 + anchors.topMargin: 2 + anchors.top: statusContainer.bottom + anchors.horizontalCenter: parent.horizontalCenter + visible: false + Component.onCompleted: + { + var top = logsContainer; + while (top.parent) + top = top.parent + var coordinates = logsContainer.mapToItem(top, 0, 0) + logsContainer.parent = top; + logsContainer.x = coordinates.x + logsContainer.y = coordinates.y + } + LogsPane + { + id: logPane + } + states: [ + State { + name: "opened"; + PropertyChanges { target: logsContainer; height: 500; visible: true } + }, + State { + name: "closed"; + PropertyChanges { target: logsContainer; height: 0; visible: false } + } + ] + transitions: Transition { + NumberAnimation { properties: "height"; easing.type: Easing.InOutQuad; duration: 200 } + NumberAnimation { properties: "visible"; easing.type: Easing.InOutQuad; duration: 200 } + } + } } Button @@ -140,7 +200,6 @@ Rectangle { id: logslink anchors.left: statusContainer.right anchors.leftMargin: 9 - visible: false anchors.verticalCenter: parent.verticalCenter action: displayLogAction iconSource: "qrc:/qml/img/search_filled.png" @@ -150,7 +209,9 @@ Rectangle { id: displayLogAction tooltip: qsTr("Display Log") onTriggered: { - mainContent.displayCompilationErrorIfAny(); + logsContainer.toggle(); + //if (status.state === "error" && logPane.front().type === "run") + // mainContent.displayCompilationErrorIfAny(); } } diff --git a/mix/qml/WebPreview.qml b/mix/qml/WebPreview.qml index 6f03088a4..5a9673a2c 100644 --- a/mix/qml/WebPreview.qml +++ b/mix/qml/WebPreview.qml @@ -12,6 +12,9 @@ Item { id: webPreview property string pendingPageUrl: "" property bool initialized: false + signal javaScriptErrorMessage(string _content) + signal javaScriptWarningMessage(string _content) + signal javaScriptInfoMessage(string _content) function setPreviewUrl(url) { if (!initialized) @@ -240,7 +243,13 @@ Item { id: webView experimental.settings.localContentCanAccessRemoteUrls: true onJavaScriptConsoleMessage: { - console.log(sourceID + ":" + lineNumber + ":" + message); + var info = sourceID + ":" + lineNumber + ":" + message; + if (level === 0) + webPreview.javaScriptInfoMessage(info); + else if (level === 1) + webPreview.javaScriptErrorMessage(info); + else if (level === 2) + webPreview.javaScriptErrorMessage(info); } onLoadingChanged: { if (!loading) { diff --git a/mix/qml/img/broom.png b/mix/qml/img/broom.png new file mode 100644 index 0000000000000000000000000000000000000000..76a9a0e0c90f9e671c3bbe3708f610c4a67f6383 GIT binary patch literal 501 zcmVl0knEFf(l4s2f?(imaF8NZjcWVO_}LI%a>%36+i~6T z^SQ6|3LtJhk^&Jz2utc^beF3b$swE(%#*EL^P}y!?)c0Gl3UrZ22;|P1>z-6qMhu) zSc&&J#k}OZ**r#`z41$?i>u|{BT{dCD|@ZL88aFoS+UGi|GK$H7_unV$Vw4NT7uEW zm-O+BfvnnV%>p9leRp*vN5T*w=e|-;#;Y22rS3<>&kwgnT?axGT}>SG8^x1s zXTOhJzePzd)sOKKbFkQxtFOOsE(7vswE3NB+UWECyT$!^v$yiJJ8C4)yjTAu`iq|U znj1Gy^<=#NA2xGJ-09bUCmJd(slT_!U2=U>^tx#e4LTY`t)UgF>el6QYc#rKzSe0f2{dxJC@=|jIIuvN`^^-?W_(@hk>V0_bL9qml/img/exit.png qml/img/run.png qml/img/note.png + qml/LogsPane.qml + qml/LogsWindow.qml + qml/img/copy.png + qml/img/broom.png diff --git a/mix/sortfilterproxymodel.cpp b/mix/sortfilterproxymodel.cpp new file mode 100644 index 000000000..75a50ea59 --- /dev/null +++ b/mix/sortfilterproxymodel.cpp @@ -0,0 +1,170 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "sortfilterproxymodel.h" +#include +#include + +using namespace dev::mix; + +SortFilterProxyModel::SortFilterProxyModel(QObject *parent) : QSortFilterProxyModel(parent) +{ + connect(this, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SIGNAL(countChanged())); + connect(this, SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SIGNAL(countChanged())); +} + +int SortFilterProxyModel::count() const +{ + return rowCount(); +} + +QObject *SortFilterProxyModel::source() const +{ + return sourceModel(); +} + +void SortFilterProxyModel::setSource(QObject *source) +{ + setSourceModel(qobject_cast(source)); +} + +QByteArray SortFilterProxyModel::sortRole() const +{ + return roleNames().value(QSortFilterProxyModel::sortRole()); +} + +void SortFilterProxyModel::setSortRole(const QByteArray &role) +{ + QSortFilterProxyModel::setSortRole(roleKey(role)); +} + +void SortFilterProxyModel::setSortOrder(Qt::SortOrder order) +{ + QSortFilterProxyModel::sort(0, order); +} + +QByteArray SortFilterProxyModel::filterRole() const +{ + return roleNames().value(QSortFilterProxyModel::filterRole()); +} + +void SortFilterProxyModel::setFilterRole(const QByteArray &role) +{ + QSortFilterProxyModel::setFilterRole(roleKey(role)); +} + +QString SortFilterProxyModel::filterString() const +{ + return filterRegExp().pattern(); +} + +void SortFilterProxyModel::setFilterString(const QString &filter) +{ + setFilterRegExp(QRegExp(filter, filterCaseSensitivity(), static_cast(filterSyntax()))); +} + +SortFilterProxyModel::FilterSyntax SortFilterProxyModel::filterSyntax() const +{ + return static_cast(filterRegExp().patternSyntax()); +} + +void SortFilterProxyModel::setFilterSyntax(SortFilterProxyModel::FilterSyntax syntax) +{ + setFilterRegExp(QRegExp(filterString(), filterCaseSensitivity(), static_cast(syntax))); +} + +QJSValue SortFilterProxyModel::get(int idx) const +{ + QJSEngine *engine = qmlEngine(this); + QJSValue value = engine->newObject(); + if (idx >= 0 && idx < count()) { + QHash roles = roleNames(); + QHashIterator it(roles); + while (it.hasNext()) { + it.next(); + value.setProperty(QString::fromUtf8(it.value()), data(index(idx, 0), it.key()).toString()); + } + } + return value; +} + +int SortFilterProxyModel::roleKey(const QByteArray &role) const +{ + QHash roles = roleNames(); + QHashIterator it(roles); + while (it.hasNext()) { + it.next(); + if (it.value() == role) + return it.key(); + } + return -1; +} + +QHash SortFilterProxyModel::roleNames() const +{ + if (QAbstractItemModel *source = sourceModel()) + return source->roleNames(); + return QHash(); +} + +bool SortFilterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const +{ + QRegExp rx = filterRegExp(); + if (rx.isEmpty()) + return true; + QAbstractItemModel *model = sourceModel(); + if (filterRole().isEmpty()) { + QHash roles = roleNames(); + QHashIterator it(roles); + while (it.hasNext()) { + it.next(); + QModelIndex sourceIndex = model->index(sourceRow, 0, sourceParent); + QString key = model->data(sourceIndex, it.key()).toString(); + if (key.contains(rx)) + return true; + } + return false; + } + QModelIndex sourceIndex = model->index(sourceRow, 0, sourceParent); + if (!sourceIndex.isValid()) + return true; + QString key = model->data(sourceIndex, roleKey(filterRole())).toString(); + return key.contains(rx); +} diff --git a/mix/sortfilterproxymodel.h b/mix/sortfilterproxymodel.h new file mode 100644 index 000000000..2ff479413 --- /dev/null +++ b/mix/sortfilterproxymodel.h @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SORTFILTERPROXYMODEL_H +#define SORTFILTERPROXYMODEL_H + +#include +#include + +namespace dev +{ +namespace mix +{ + +class SortFilterProxyModel : public QSortFilterProxyModel +{ + Q_OBJECT + Q_PROPERTY(int count READ count NOTIFY countChanged) + Q_PROPERTY(QObject *source READ source WRITE setSource) + + Q_PROPERTY(QByteArray sortRole READ sortRole WRITE setSortRole) + Q_PROPERTY(Qt::SortOrder sortOrder READ sortOrder WRITE setSortOrder) + + Q_PROPERTY(QByteArray filterRole READ filterRole WRITE setFilterRole) + Q_PROPERTY(QString filterString READ filterString WRITE setFilterString) + Q_PROPERTY(FilterSyntax filterSyntax READ filterSyntax WRITE setFilterSyntax) + + Q_ENUMS(FilterSyntax) + +public: + explicit SortFilterProxyModel(QObject *parent = 0); + + QObject *source() const; + void setSource(QObject *source); + + QByteArray sortRole() const; + void setSortRole(const QByteArray &role); + + void setSortOrder(Qt::SortOrder order); + + QByteArray filterRole() const; + void setFilterRole(const QByteArray &role); + + QString filterString() const; + void setFilterString(const QString &filter); + + enum FilterSyntax { + RegExp, + Wildcard, + FixedString + }; + + FilterSyntax filterSyntax() const; + void setFilterSyntax(FilterSyntax syntax); + + int count() const; + Q_INVOKABLE QJSValue get(int index) const; + +signals: + void countChanged(); + +protected: + int roleKey(const QByteArray &role) const; + QHash roleNames() const; + bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const; +}; + +} +} +#endif // SORTFILTERPROXYMODEL_H From b0203717433580c88384fb9d748ca0af66dc3834 Mon Sep 17 00:00:00 2001 From: yann300 Date: Fri, 6 Mar 2015 14:46:29 +0100 Subject: [PATCH 033/100] - UI changes - Filter by message type --- mix/qml/LogsPane.qml | 213 +++++++++++++++++++++++++++++------------ mix/qml/StatusPane.qml | 27 +++++- mix/res.qrc | 1 - 3 files changed, 178 insertions(+), 63 deletions(-) diff --git a/mix/qml/LogsPane.qml b/mix/qml/LogsPane.qml index 63d940eb6..9fa15f250 100644 --- a/mix/qml/LogsPane.qml +++ b/mix/qml/LogsPane.qml @@ -1,6 +1,7 @@ import QtQuick 2.0 import QtQuick.Layouts 1.0 import QtQuick.Controls 1.1 +import QtQuick.Controls.Styles 1.3 import org.ethereum.qml.SortFilterProxyModel 1.0 import "." @@ -15,10 +16,10 @@ Rectangle radius: 5 color: "#f7f7f7" border.color: "#808080" - border.width: 2 + border.width: 1 ColumnLayout { - height: parent.height - 4 - width: parent.width - 2 + height: parent.height + width: parent.width spacing: 0 Row { @@ -62,82 +63,172 @@ Rectangle for (var k = 0; k < logsModel.count; k++) { var log = logsModel.get(k); - content += log.type + "\t" + log.level + "\t" + log.date + "\t" + log.content; + content += log.type + "\t" + log.level + "\t" + log.date + "\t" + log.content + "\n"; } appContext.toClipboard(content); } } - } - - ListModel { - id: logsModel - } - TableView { - id: logsTable - clip: true - Layout.fillWidth: true - Layout.preferredHeight: parent.height - rowAction.height - headerVisible : false - onDoubleClicked: - { - var log = logsModel.get((logsTable.currentRow)); - appContext.toClipboard(log.type + " " + log.level + " " + log.date + " " + log.content); + Rectangle { + anchors.verticalCenter: parent.verticalCenter + width: 1; + height: parent.height - 10 + color : "#808080" } - model: SortFilterProxyModel { - id: proxyModel - source: logsModel + ToolButton { + id: javascriptButton + checkable: true + height: 30 + anchors.verticalCenter: parent.verticalCenter + checked: true + onCheckedChanged: { + proxyModel.toogleFilter("javascript") + } + tooltip: qsTr("JavaScript") + style: + ButtonStyle { + label: + Item { + DefaultLabel { + font.family: "sans serif" + font.pointSize: Style.absoluteSize(-3) + color: "#5391d8" + anchors.centerIn: parent + text: qsTr("JavaScript") + } + } + } + } - filterRole: "" - filterString: "" - filterSyntax: SortFilterProxyModel.Wildcard - filterCaseSensitivity: Qt.CaseInsensitive + ToolButton { + id: runButton + checkable: true + height: 30 + anchors.verticalCenter: parent.verticalCenter + checked: true + onCheckedChanged: { + proxyModel.toogleFilter("run") } - TableViewColumn - { - role: "date" - title: qsTr("date") - width: 150 - delegate: itemDelegate + tooltip: qsTr("Run") + style: + ButtonStyle { + label: + Item { + DefaultLabel { + font.family: "sans serif" + font.pointSize: Style.absoluteSize(-3) + color: "#5391d8" + anchors.centerIn: parent + text: qsTr("Run") + } + } } - TableViewColumn - { - role: "type" - title: qsTr("type") - width: 100 - delegate: itemDelegate + } + + ToolButton { + id: stateButton + checkable: true + height: 30 + anchors.verticalCenter: parent.verticalCenter + checked: true + onCheckedChanged: { + proxyModel.toogleFilter("state") + } + tooltip: qsTr("State") + style: + ButtonStyle { + label: + Item { + DefaultLabel { + font.family: "sans serif" + font.pointSize: Style.absoluteSize(-3) + color: "#5391d8" + anchors.centerIn: parent + text: qsTr("State") + } + } } - TableViewColumn + } + } + + ListModel { + id: logsModel + } + + TableView { + id: logsTable + clip: true + Layout.fillWidth: true + Layout.preferredHeight: parent.height - rowAction.height + headerVisible : false + onDoubleClicked: + { + var log = logsModel.get((logsTable.currentRow)); + appContext.toClipboard(log.type + " " + log.level + " " + log.date + " " + log.content); + } + + model: SortFilterProxyModel { + id: proxyModel + source: logsModel + + function toogleFilter(_value) { - role: "content" - title: qsTr("content") - width: 700 - delegate: itemDelegate + if (filterString.indexOf('_' + _value) !== -1) + filterString = filterString.replace('_' + _value, _value); + else + filterString = filterString.replace(_value, '_' + _value); } + + filterRole: "type" + filterString: "(?:javascript|run|state)" + filterSyntax: SortFilterProxyModel.RegExp + filterCaseSensitivity: Qt.CaseInsensitive } + TableViewColumn + { + role: "date" + title: qsTr("date") + width: 150 + delegate: itemDelegate + } + TableViewColumn + { + role: "type" + title: qsTr("type") + width: 100 + delegate: itemDelegate + } + TableViewColumn + { + role: "content" + title: qsTr("content") + width: 700 + delegate: itemDelegate + } + } - Component { - id: itemDelegate - DefaultLabel { - text: styleData.value; - font.family: "sans serif" - font.pointSize: Style.absoluteSize(-1) - color: { - if (styleData.row > -1) - { - var l = logsModel.get(styleData.row).level - if (l === "error") - return "red" - else if (l === "warning") - return "orange" - else if (l === "info") - return "#808080" - } - else + Component { + id: itemDelegate + DefaultLabel { + text: styleData.value; + font.family: "sans serif" + font.pointSize: Style.absoluteSize(-1) + color: { + if (styleData.row > -1) + { + var l = logsModel.get(styleData.row).level + if (l === "error") + return "red" + else if (l === "warning") + return "orange" + else if (l === "info") return "#808080" } + else + return "#808080" } } } } +} diff --git a/mix/qml/StatusPane.qml b/mix/qml/StatusPane.qml index 87bfb39d5..141d45a48 100644 --- a/mix/qml/StatusPane.qml +++ b/mix/qml/StatusPane.qml @@ -87,6 +87,24 @@ Rectangle { width: 500 height: 30 color: "#fcfbfc" + states: [ + State { + name: "logsOpened" + PropertyChanges { + target: statusContainer + border.color: "#808080" + border.width: 1 + } + }, + State { + name: "logsClosed" + PropertyChanges { + target: statusContainer + border.color: "#808080" + border.width: 0 + } + } + ] Text { anchors.verticalCenter: parent.verticalCenter @@ -152,18 +170,25 @@ Rectangle { function toggle() { if (logsContainer.state === "opened") + { + statusContainer.state = "logsClosed"; logsContainer.state = "closed" + } else + { + statusContainer.state = "logsOpened"; logsContainer.state = "opened"; + } } id: logsContainer width: 1000 height: 0 - anchors.topMargin: 2 + //anchors.topMargin: anchors.top: statusContainer.bottom anchors.horizontalCenter: parent.horizontalCenter visible: false + radius: 5 Component.onCompleted: { var top = logsContainer; diff --git a/mix/res.qrc b/mix/res.qrc index 4e5719a18..5c706fb9c 100644 --- a/mix/res.qrc +++ b/mix/res.qrc @@ -110,7 +110,6 @@ qml/img/run.png qml/img/note.png qml/LogsPane.qml - qml/LogsWindow.qml qml/img/copy.png qml/img/broom.png From d6c37b7562e9be68d1d8a4570354738b4d46c3ad Mon Sep 17 00:00:00 2001 From: subtly Date: Fri, 6 Mar 2015 16:14:02 +0100 Subject: [PATCH 034/100] don't deallocate keys --- libp2p/RLPxFrameIO.cpp | 17 ++++++++++------- libp2p/RLPxFrameIO.h | 7 +++++-- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/libp2p/RLPxFrameIO.cpp b/libp2p/RLPxFrameIO.cpp index 8531759e9..e9d3715a2 100644 --- a/libp2p/RLPxFrameIO.cpp +++ b/libp2p/RLPxFrameIO.cpp @@ -57,16 +57,19 @@ RLPXFrameIO::RLPXFrameIO(RLPXHandshake const& _init): m_socket(_init.m_socket) // aes-secret = sha3(ecdhe-shared-secret || shared-secret) sha3(keyMaterial, outRef); // output aes-secret - SecByteBlock aesSecretEnc(outRef.data(), h128::size); - SecByteBlock aesSecretDec(outRef.data(), h128::size); - SecByteBlock emptyIV(h128::size); - m_frameEnc.SetKeyWithIV(aesSecretEnc, h128::size, emptyIV); - m_frameDec.SetKeyWithIV(aesSecretDec, h128::size, emptyIV); + m_frameEncKey.resize(h128::size); + memcpy(m_frameEncKey.data(), outRef.data(), h128::size); + m_frameDecKey.resize(h128::size); + memcpy(m_frameDecKey.data(), outRef.data(), h128::size); + h128 iv; + m_frameEnc.SetKeyWithIV(m_frameEncKey, h128::size, iv.data()); + m_frameDec.SetKeyWithIV(m_frameDecKey, h128::size, iv.data()); // mac-secret = sha3(ecdhe-shared-secret || aes-secret) sha3(keyMaterial, outRef); // output mac-secret - SecByteBlock macSecret(outRef.data(), h128::size); - m_macEnc.SetKey(macSecret, h128::size); + m_macEncKey.resize(h128::size); + memcpy(m_macEncKey.data(), outRef.data(), h128::size); + m_macEnc.SetKey(m_macEncKey, h128::size); // Initiator egress-mac: sha3(mac-secret^recipient-nonce || auth-sent-init) // ingress-mac: sha3(mac-secret^initiator-nonce || auth-recvd-ack) diff --git a/libp2p/RLPxFrameIO.h b/libp2p/RLPxFrameIO.h index a8b2011eb..2bd2218aa 100644 --- a/libp2p/RLPxFrameIO.h +++ b/libp2p/RLPxFrameIO.h @@ -86,15 +86,18 @@ protected: private: void updateMAC(CryptoPP::SHA3_256& _mac, bytesConstRef _seed = bytesConstRef()); + CryptoPP::SecByteBlock m_frameEncKey; + CryptoPP::SecByteBlock m_frameDecKey; CryptoPP::CTR_Mode::Encryption m_frameEnc; CryptoPP::CTR_Mode::Encryption m_frameDec; - Mutex x_macEnc; + CryptoPP::SecByteBlock m_macEncKey; CryptoPP::ECB_Mode::Encryption m_macEnc; + Mutex x_macEnc; CryptoPP::SHA3_256 m_egressMac; CryptoPP::SHA3_256 m_ingressMac; std::shared_ptr m_socket; }; - + } } \ No newline at end of file From 339e4aec3352e25b84a4ff48616644f9a4354298 Mon Sep 17 00:00:00 2001 From: subtly Date: Fri, 6 Mar 2015 16:48:58 +0100 Subject: [PATCH 035/100] offset logic for eth packets --- libethereum/EthereumPeer.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/libethereum/EthereumPeer.cpp b/libethereum/EthereumPeer.cpp index a546c3b22..58cc5ca72 100644 --- a/libethereum/EthereumPeer.cpp +++ b/libethereum/EthereumPeer.cpp @@ -324,8 +324,8 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r) case GetTransactionsPacket: break; // DEPRECATED. case TransactionsPacket: { - clogS(NetMessageSummary) << "Transactions (" << dec << (_r.itemCount() - 1) << "entries)"; - addRating(_r.itemCount() - 1); + clogS(NetMessageSummary) << "Transactions (" << dec << _r.itemCount() << "entries)"; + addRating(_r.itemCount()); Guard l(x_knownTransactions); for (unsigned i = 0; i < _r.itemCount(); ++i) { @@ -355,14 +355,14 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r) } case BlockHashesPacket: { - clogS(NetMessageSummary) << "BlockHashes (" << dec << (_r.itemCount() - 1) << "entries)" << (_r.itemCount() - 1 ? "" : ": NoMoreHashes"); + clogS(NetMessageSummary) << "BlockHashes (" << dec << _r.itemCount() << "entries)" << (_r.itemCount() ? "" : ": NoMoreHashes"); if (m_asking != Asking::Hashes) { cwarn << "Peer giving us hashes when we didn't ask for them."; break; } - if (_r.itemCount() == 1) + if (_r.itemCount() == 0) { transition(Asking::Blocks); return true; @@ -384,7 +384,7 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r) } case GetBlocksPacket: { - clogS(NetMessageSummary) << "GetBlocks (" << dec << (_r.itemCount() - 1) << "entries)"; + clogS(NetMessageSummary) << "GetBlocks (" << dec << _r.itemCount() << "entries)"; // return the requested blocks. bytes rlp; unsigned n = 0; @@ -404,12 +404,12 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r) } case BlocksPacket: { - clogS(NetMessageSummary) << "Blocks (" << dec << (_r.itemCount() - 1) << "entries)" << (_r.itemCount() - 1 ? "" : ": NoMoreBlocks"); + clogS(NetMessageSummary) << "Blocks (" << dec << _r.itemCount() << "entries)" << (_r.itemCount() ? "" : ": NoMoreBlocks"); if (m_asking != Asking::Blocks) clogS(NetWarn) << "Unexpected Blocks received!"; - if (_r.itemCount() == 1) + if (_r.itemCount() == 0) { // Got to this peer's latest block - just give up. transition(Asking::Nothing); @@ -470,7 +470,7 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r) auto h = BlockInfo::headerHash(_r[0].data()); clogS(NetMessageSummary) << "NewBlock: " << h.abridged(); - if (_r.itemCount() != 3) + if (_r.itemCount() != 2) disable("NewBlock without 2 data fields."); else { From 71ccb9c62b36a5ca4289968d3b6bcc40a2dd51f6 Mon Sep 17 00:00:00 2001 From: subtly Date: Fri, 6 Mar 2015 17:04:29 +0100 Subject: [PATCH 036/100] curlies --- libdevcrypto/CryptoPP.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/libdevcrypto/CryptoPP.cpp b/libdevcrypto/CryptoPP.cpp index 356317935..77893736f 100644 --- a/libdevcrypto/CryptoPP.cpp +++ b/libdevcrypto/CryptoPP.cpp @@ -55,13 +55,13 @@ bytes Secp256k1::eciesKDF(Secret _z, bytes _s1, unsigned kdByteLen) k.reserve(k.size() + h256::size); move(digest.begin(), digest.end(), back_inserter(k)); - if (ctr[3]++ && ctr[3] != 0) { + if (ctr[3]++ && ctr[3] != 0) continue; - } else if (ctr[2]++ && ctr[2] != 0) { + else if (ctr[2]++ && ctr[2] != 0) continue; - } else if (ctr[1]++ && ctr[1] != 0) { + else if (ctr[1]++ && ctr[1] != 0) continue; - } else + else ctr[0]++; } @@ -85,7 +85,7 @@ void Secp256k1::encryptECIES(Public const& _k, bytes& io_cipher) bytes cipherText; encryptSymNoAuth(*(Secret*)eKey.data(), bytesConstRef(&io_cipher), cipherText, h128()); - if (!cipherText.size()) + if (cipherText.empty()) return; bytes msg(1 + Public::size + h128::size + cipherText.size() + 32); From 4955f33797598c6c853c6dac360a4c79398b5d29 Mon Sep 17 00:00:00 2001 From: subtly Date: Fri, 6 Mar 2015 17:07:15 +0100 Subject: [PATCH 037/100] -whitespace --- libp2p/Peer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libp2p/Peer.h b/libp2p/Peer.h index 743d941db..8774b6578 100644 --- a/libp2p/Peer.h +++ b/libp2p/Peer.h @@ -29,7 +29,7 @@ namespace dev namespace p2p { - + /** * @brief Representation of connectivity state and all other pertinent Peer metadata. * A Peer represents connectivity between two nodes, which in this case, are the host From fc1ea097aab2120f9e38f296c948d6a6904038da Mon Sep 17 00:00:00 2001 From: subtly Date: Sat, 7 Mar 2015 00:09:36 +0100 Subject: [PATCH 038/100] packet-type interop w/go --- libp2p/Host.cpp | 19 ++++++------------- libp2p/Host.h | 2 -- libp2p/HostCapability.cpp | 5 ----- libp2p/HostCapability.h | 2 -- libp2p/RLPxFrameIO.cpp | 12 ++++++------ libp2p/RLPxHandshake.cpp | 11 +++++++---- libp2p/Session.cpp | 8 +------- 7 files changed, 20 insertions(+), 39 deletions(-) diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index eb5e8adc1..5377d80b8 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -154,7 +154,7 @@ void Host::doneWorking() unsigned Host::protocolVersion() const { - return 4; + return 3; } bool Host::startPeerSession(Public const& _id, RLP const& _rlp, RLPXFrameIO* _io, bi::tcp::endpoint _endpoint) @@ -174,10 +174,10 @@ bool Host::startPeerSession(Public const& _id, RLP const& _rlp, RLPXFrameIO* _io // TODO: update pendingconns w/session-weak-ptr for graceful shutdown (otherwise this line isn't safe) p->endpoint.tcp.address(_endpoint.address()); - auto protocolVersion = _rlp[1].toInt(); - auto clientVersion = _rlp[2].toString(); - auto caps = _rlp[3].toVector(); - auto listenPort = _rlp[4].toInt(); + auto protocolVersion = _rlp[0].toInt(); + auto clientVersion = _rlp[1].toString(); + auto caps = _rlp[2].toVector(); + auto listenPort = _rlp[3].toInt(); // clang error (previously: ... << hex << caps ...) // "'operator<<' should be declared prior to the call site or in an associated namespace of one of its arguments" @@ -187,7 +187,7 @@ bool Host::startPeerSession(Public const& _id, RLP const& _rlp, RLPXFrameIO* _io clog(NetMessageSummary) << "Hello: " << clientVersion << "V[" << protocolVersion << "]" << _id.abridged() << showbase << capslog.str() << dec << listenPort; // create session so disconnects are managed - auto ps = make_shared(this, _io, p, PeerSessionInfo({_id, clientVersion, _endpoint.address().to_string(), listenPort, chrono::steady_clock::duration(), _rlp[3].toSet(), 0, map()})); + auto ps = make_shared(this, _io, p, PeerSessionInfo({_id, clientVersion, _endpoint.address().to_string(), listenPort, chrono::steady_clock::duration(), _rlp[2].toSet(), 0, map()})); if (protocolVersion != this->protocolVersion()) { ps->disconnect(IncompatibleProtocol); @@ -203,8 +203,6 @@ bool Host::startPeerSession(Public const& _id, RLP const& _rlp, RLPXFrameIO* _io ps->disconnect(DuplicatePeer); return false; } - - clog(NetNote) << "p2p.host.peer.register" << _id.abridged(); m_sessions[_id] = ps; } ps->start(); @@ -217,7 +215,6 @@ bool Host::startPeerSession(Public const& _id, RLP const& _rlp, RLPXFrameIO* _io } clog(NetNote) << "p2p.host.peer.register" << _id.abridged(); StructuredLogger::p2pConnected(_id.abridged(), ps->m_peer->peerEndpoint(), ps->m_peer->m_lastConnected, clientVersion, peerCount()); - return true; } @@ -271,10 +268,6 @@ void Host::onNodeTableEvent(NodeId const& _n, NodeTableEventType const& _e) } } -void Host::seal(bytes& _b) -{ -} - void Host::determinePublic(string const& _publicAddress, bool _upnp) { m_peerAddresses.clear(); diff --git a/libp2p/Host.h b/libp2p/Host.h index 55f4d61b0..00fcc0943 100644 --- a/libp2p/Host.h +++ b/libp2p/Host.h @@ -176,8 +176,6 @@ private: /// Called only from startedWorking(). void runAcceptor(); - void seal(bytes& _b); - /// Called by Worker. Not thread-safe; to be called only by worker. virtual void startedWorking(); /// Called by startedWorking. Not thread-safe; to be called only be Worker. diff --git a/libp2p/HostCapability.cpp b/libp2p/HostCapability.cpp index 9437cd45c..b2acdcd1b 100644 --- a/libp2p/HostCapability.cpp +++ b/libp2p/HostCapability.cpp @@ -27,11 +27,6 @@ using namespace std; using namespace dev; using namespace dev::p2p; -void HostCapabilityFace::seal(bytes& _b) -{ - m_host->seal(_b); -} - std::vector,std::shared_ptr>> HostCapabilityFace::peerSessions() const { RecursiveGuard l(m_host->x_sessions); diff --git a/libp2p/HostCapability.h b/libp2p/HostCapability.h index 9122ca1fa..93086b1c9 100644 --- a/libp2p/HostCapability.h +++ b/libp2p/HostCapability.h @@ -57,8 +57,6 @@ protected: virtual void onStarting() {} virtual void onStopping() {} - void seal(bytes& _b); - private: Host* m_host = nullptr; }; diff --git a/libp2p/RLPxFrameIO.cpp b/libp2p/RLPxFrameIO.cpp index e9d3715a2..b5185cd43 100644 --- a/libp2p/RLPxFrameIO.cpp +++ b/libp2p/RLPxFrameIO.cpp @@ -96,7 +96,6 @@ void RLPXFrameIO::writeSingleFramePacket(bytesConstRef _packet, bytes& o_bytes) { // _packet = type || rlpList() - // current/old packet format: prep(_s).appendList(_args + 1).append((unsigned)_id); RLPStream header; uint32_t len = (uint32_t)_packet.size(); header.appendRaw(bytes({byte((len >> 16) & 0xff), byte((len >> 8) & 0xff), byte(len & 0xff)})); @@ -105,10 +104,10 @@ void RLPXFrameIO::writeSingleFramePacket(bytesConstRef _packet, bytes& o_bytes) // TODO: SECURITY check that header is <= 16 bytes - bytes headerWithMac(32); - bytes headerBytes(16); - bytesConstRef(&header.out()).copyTo(&headerBytes); - m_frameEnc.ProcessData(headerWithMac.data(), headerBytes.data(), 16); + bytes headerWithMac; + header.swapOut(headerWithMac); + headerWithMac.resize(32); + m_frameEnc.ProcessData(headerWithMac.data(), headerWithMac.data(), 16); updateEgressMACWithHeader(bytesConstRef(&headerWithMac).cropped(0, 16)); egressDigest().ref().copyTo(bytesRef(&headerWithMac).cropped(h128::size,h128::size)); @@ -131,7 +130,8 @@ bool RLPXFrameIO::authAndDecryptHeader(bytesRef io) asserts(io.size() == h256::size); updateIngressMACWithHeader(io); bytesConstRef macRef = io.cropped(h128::size, h128::size); - if (*(h128*)macRef.data() != ingressDigest()) + h128 expected = ingressDigest(); + if (*(h128*)macRef.data() != expected) return false; m_frameDec.ProcessData(io.data(), io.data(), h128::size); return true; diff --git a/libp2p/RLPxHandshake.cpp b/libp2p/RLPxHandshake.cpp index 004880c39..a1ede6056 100644 --- a/libp2p/RLPxHandshake.cpp +++ b/libp2p/RLPxHandshake.cpp @@ -171,7 +171,7 @@ void RLPXHandshake::transition(boost::system::error_code _ech) // old packet format // 5 arguments, HelloPacket RLPStream s; - s.appendList(5 + 1).append((unsigned)0) + s.append((unsigned)0).appendList(5) << m_host->protocolVersion() << m_host->m_clientVersion << m_host->caps() @@ -233,7 +233,8 @@ void RLPXHandshake::transition(boost::system::error_code _ech) transition(ec); else { - if (!m_io->authAndDecryptFrame(bytesRef(&m_handshakeInBuffer))) + bytesRef frame(&m_handshakeInBuffer); + if (!m_io->authAndDecryptFrame(frame)) { clog(NetWarn) << (m_originated ? "p2p.connect.egress" : "p2p.connect.ingress") << "hello frame: decrypt failed"; m_nextState = Error; @@ -241,8 +242,7 @@ void RLPXHandshake::transition(boost::system::error_code _ech) return; } - RLP rlp(m_handshakeInBuffer); - auto packetType = (PacketType)rlp[0].toInt(); + PacketType packetType = (PacketType)(frame[0] == 0x80 ? 0x0 : frame[0]); if (packetType != 0) { clog(NetWarn) << (m_originated ? "p2p.connect.egress" : "p2p.connect.ingress") << "hello frame: invalid packet type"; @@ -251,6 +251,9 @@ void RLPXHandshake::transition(boost::system::error_code _ech) return; } + clog(NetNote) << (m_originated ? "p2p.connect.egress" : "p2p.connect.ingress") << "hello frame: success. starting session."; + + RLP rlp(frame.cropped(1)); m_host->startPeerSession(m_remote, rlp, m_io, m_socket->remoteEndpoint()); } }); diff --git a/libp2p/Session.cpp b/libp2p/Session.cpp index da83c0b5e..5ceb61f8b 100644 --- a/libp2p/Session.cpp +++ b/libp2p/Session.cpp @@ -278,14 +278,13 @@ void Session::ping() RLPStream& Session::prep(RLPStream& _s, PacketType _id, unsigned _args) { - return _s.appendRaw(bytes(1, _id)).appendList(_args); + return _s.append((unsigned)_id).appendList(_args); } void Session::sealAndSend(RLPStream& _s) { bytes b; _s.swapOut(b); - m_server->seal(b); send(move(b)); } @@ -301,11 +300,6 @@ bool Session::checkPacket(bytesConstRef _msg) return true; } -//void Session::send(bytesConstRef _msg) -//{ -// send(_msg.toBytes()); -//} - void Session::send(bytes&& _msg) { clogS(NetLeft) << RLP(bytesConstRef(&_msg).cropped(1)); From 8ce4e370169ed716d6f440b81f105ca31842c539 Mon Sep 17 00:00:00 2001 From: CJentzsch Date: Sat, 7 Mar 2015 14:18:22 +0100 Subject: [PATCH 039/100] add suiciderefund tests --- test/stRefundTestFiller.json | 89 +++++++++++++++++++++++++++++++ test/stTransactionTestFiller.json | 4 +- 2 files changed, 91 insertions(+), 2 deletions(-) diff --git a/test/stRefundTestFiller.json b/test/stRefundTestFiller.json index 9e867a508..b0d0e1747 100644 --- a/test/stRefundTestFiller.json +++ b/test/stRefundTestFiller.json @@ -304,6 +304,95 @@ "data" : "" } }, + + "refund_CallToSuicide" : { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "10000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "1000000000000000000", + "nonce" : "0", + "code" : "{ [[ 0 ]] (CALL 500 0xaaae7baea6a6c7c4c2dfeb977efac326af552aaa 0 0 0 0 0 )}", + "storage" : { + "0x01" : "0x01" + } + }, + "aaae7baea6a6c7c4c2dfeb977efac326af552aaa" : { + "balance" : "1000000000000000000", + "nonce" : "0", + "code" : "{ (SUICIDE 0x095e7baea6a6c7c4c2dfeb977efac326af552d87) }", + "storage" : { + "0x01" : "0x01" + } + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "100000000", + "nonce" : "0", + "code" : "", + "storage": {} + } + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "10000000", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "10", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + + "refund_CallToSuicideTwice" : { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "10000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "1000000000000000000", + "nonce" : "0", + "code" : "{ [[ 0 ]] (CALL 500 0xaaae7baea6a6c7c4c2dfeb977efac326af552aaa 0 0 0 0 0 ) (CALL 500 0xaaae7baea6a6c7c4c2dfeb977efac326af552aaa 0 0 0 0 0 )}", + "storage" : { + "0x01" : "0x01" + } + }, + "aaae7baea6a6c7c4c2dfeb977efac326af552aaa" : { + "balance" : "1000000000000000000", + "nonce" : "0", + "code" : "{ (SUICIDE 0x095e7baea6a6c7c4c2dfeb977efac326af552d87) }", + "storage" : { + "0x01" : "0x01" + } + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "100000000", + "nonce" : "0", + "code" : "", + "storage": {} + } + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "10000000", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "10", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + "refund_CallA" : { "env" : { "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", diff --git a/test/stTransactionTestFiller.json b/test/stTransactionTestFiller.json index 1299e6d94..b6ddbb2f9 100644 --- a/test/stTransactionTestFiller.json +++ b/test/stTransactionTestFiller.json @@ -584,7 +584,7 @@ "pre" : { "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { - "balance" : "100000", + "balance" : "180000", "code" : "", "nonce" : "0", "storage" : { @@ -612,7 +612,7 @@ "transaction" : { "data" : "", - "gasLimit" : "50000", + "gasLimit" : "150000", "gasPrice" : "1", "nonce" : "", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", From 778b1802827798c94ba3df19193a0a7c8ec7570a Mon Sep 17 00:00:00 2001 From: subtly Date: Sat, 7 Mar 2015 18:36:27 +0100 Subject: [PATCH 040/100] aes256, as per the spec. --- libp2p/RLPxFrameIO.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/libp2p/RLPxFrameIO.cpp b/libp2p/RLPxFrameIO.cpp index b5185cd43..a3b3b5613 100644 --- a/libp2p/RLPxFrameIO.cpp +++ b/libp2p/RLPxFrameIO.cpp @@ -57,19 +57,19 @@ RLPXFrameIO::RLPXFrameIO(RLPXHandshake const& _init): m_socket(_init.m_socket) // aes-secret = sha3(ecdhe-shared-secret || shared-secret) sha3(keyMaterial, outRef); // output aes-secret - m_frameEncKey.resize(h128::size); - memcpy(m_frameEncKey.data(), outRef.data(), h128::size); - m_frameDecKey.resize(h128::size); - memcpy(m_frameDecKey.data(), outRef.data(), h128::size); + m_frameEncKey.resize(h256::size); + memcpy(m_frameEncKey.data(), outRef.data(), h256::size); + m_frameDecKey.resize(h256::size); + memcpy(m_frameDecKey.data(), outRef.data(), h256::size); h128 iv; - m_frameEnc.SetKeyWithIV(m_frameEncKey, h128::size, iv.data()); - m_frameDec.SetKeyWithIV(m_frameDecKey, h128::size, iv.data()); + m_frameEnc.SetKeyWithIV(m_frameEncKey, h256::size, iv.data()); + m_frameDec.SetKeyWithIV(m_frameDecKey, h256::size, iv.data()); // mac-secret = sha3(ecdhe-shared-secret || aes-secret) sha3(keyMaterial, outRef); // output mac-secret - m_macEncKey.resize(h128::size); - memcpy(m_macEncKey.data(), outRef.data(), h128::size); - m_macEnc.SetKey(m_macEncKey, h128::size); + m_macEncKey.resize(h256::size); + memcpy(m_macEncKey.data(), outRef.data(), h256::size); + m_macEnc.SetKey(m_macEncKey, h256::size); // Initiator egress-mac: sha3(mac-secret^recipient-nonce || auth-sent-init) // ingress-mac: sha3(mac-secret^initiator-nonce || auth-recvd-ack) From 5902a8c2b0676bfc9bce26d8a1d9e49a3844721d Mon Sep 17 00:00:00 2001 From: subtly Date: Sat, 7 Mar 2015 21:38:21 +0100 Subject: [PATCH 041/100] docs. better method name. remove unused code. --- libp2p/RLPxFrameIO.cpp | 18 +++----------- libp2p/RLPxFrameIO.h | 56 ++++++++++++++++++++++++++++++------------ 2 files changed, 44 insertions(+), 30 deletions(-) diff --git a/libp2p/RLPxFrameIO.cpp b/libp2p/RLPxFrameIO.cpp index a3b3b5613..2c233c1d4 100644 --- a/libp2p/RLPxFrameIO.cpp +++ b/libp2p/RLPxFrameIO.cpp @@ -120,7 +120,7 @@ void RLPXFrameIO::writeSingleFramePacket(bytesConstRef _packet, bytes& o_bytes) if (padding) m_frameEnc.ProcessData(paddingRef.data(), paddingRef.data(), padding); bytesRef packetWithPaddingRef(o_bytes.data() + 32, _packet.size() + padding); - updateEgressMACWithEndOfFrame(packetWithPaddingRef); + updateEgressMACWithFrame(packetWithPaddingRef); bytesRef macRef(o_bytes.data() + 32 + _packet.size() + padding, h128::size); egressDigest().ref().copyTo(macRef); } @@ -140,7 +140,7 @@ bool RLPXFrameIO::authAndDecryptHeader(bytesRef io) bool RLPXFrameIO::authAndDecryptFrame(bytesRef io) { bytesRef cipherText(io.cropped(0, io.size() - h128::size)); - updateIngressMACWithEndOfFrame(cipherText); + updateIngressMACWithFrame(cipherText); bytesConstRef frameMac(io.data() + io.size() - h128::size, h128::size); if (*(h128*)frameMac.data() != ingressDigest()) return false; @@ -169,15 +169,10 @@ void RLPXFrameIO::updateEgressMACWithHeader(bytesConstRef _headerCipher) updateMAC(m_egressMac, _headerCipher.cropped(0, 16)); } -void RLPXFrameIO::updateEgressMACWithEndOfFrame(bytesConstRef _cipher) +void RLPXFrameIO::updateEgressMACWithFrame(bytesConstRef _cipher) { m_egressMac.Update(_cipher.data(), _cipher.size()); updateMAC(m_egressMac); - { - SHA3_256 prev(m_egressMac); - h128 digest; - prev.TruncatedFinal(digest.data(), h128::size); - } } void RLPXFrameIO::updateIngressMACWithHeader(bytesConstRef _headerCipher) @@ -185,15 +180,10 @@ void RLPXFrameIO::updateIngressMACWithHeader(bytesConstRef _headerCipher) updateMAC(m_ingressMac, _headerCipher.cropped(0, 16)); } -void RLPXFrameIO::updateIngressMACWithEndOfFrame(bytesConstRef _cipher) +void RLPXFrameIO::updateIngressMACWithFrame(bytesConstRef _cipher) { m_ingressMac.Update(_cipher.data(), _cipher.size()); updateMAC(m_ingressMac); - { - SHA3_256 prev(m_ingressMac); - h128 digest; - prev.TruncatedFinal(digest.data(), h128::size); - } } void RLPXFrameIO::updateMAC(SHA3_256& _mac, bytesConstRef _seed) diff --git a/libp2p/RLPxFrameIO.h b/libp2p/RLPxFrameIO.h index 2bd2218aa..3a0fd35e0 100644 --- a/libp2p/RLPxFrameIO.h +++ b/libp2p/RLPxFrameIO.h @@ -38,6 +38,10 @@ namespace p2p class RLPXHandshake; +/** + * @brief Encoder/decoder transport for RLPx connections established by RLPXHandshake. + * Managed (via shared_ptr) socket for use by RLPXHandshake and RLPXFrameIO. + */ class RLPXSocket: public std::enable_shared_from_this { public: @@ -53,48 +57,68 @@ protected: bi::tcp::socket m_socket; }; +/** + * @brief Encoder/decoder transport for RLPx connections established by RLPXHandshake. + * + * Thread Safety + * Distinct Objects: Safe. + * Shared objects: Unsafe. + */ class RLPXFrameIO { friend class Session; public: + /// Constructor. + /// Requires instance of RLPXHandshake which has completed first two phases of handshake. RLPXFrameIO(RLPXHandshake const& _init); ~RLPXFrameIO() {} + /// Encrypt _packet as RLPx frame. void writeSingleFramePacket(bytesConstRef _packet, bytes& o_bytes); - /// Authenticates and decrypts header in-place. + /// Authenticate and decrypt header in-place. bool authAndDecryptHeader(bytesRef io_cipherWithMac); - /// Authenticates and decrypts frame in-place. + /// Authenticate and decrypt frame in-place. bool authAndDecryptFrame(bytesRef io_cipherWithMac); + /// Return first 16 bytes of current digest from egress mac. h128 egressDigest(); - + + /// Return first 16 bytes of current digest from ingress mac. h128 ingressDigest(); +protected: + /// Update state of egress MAC with frame header. void updateEgressMACWithHeader(bytesConstRef _headerCipher); + + /// Update state of egress MAC with frame. + void updateEgressMACWithFrame(bytesConstRef _cipher); - void updateEgressMACWithEndOfFrame(bytesConstRef _cipher); - + /// Update state of ingress MAC with frame header. void updateIngressMACWithHeader(bytesConstRef _headerCipher); - void updateIngressMACWithEndOfFrame(bytesConstRef _cipher); + /// Update state of ingress MAC with frame. + void updateIngressMACWithFrame(bytesConstRef _cipher); -protected: bi::tcp::socket& socket() { return m_socket->ref(); } private: + /// Update state of _mac. void updateMAC(CryptoPP::SHA3_256& _mac, bytesConstRef _seed = bytesConstRef()); - CryptoPP::SecByteBlock m_frameEncKey; - CryptoPP::SecByteBlock m_frameDecKey; - CryptoPP::CTR_Mode::Encryption m_frameEnc; - CryptoPP::CTR_Mode::Encryption m_frameDec; - CryptoPP::SecByteBlock m_macEncKey; - CryptoPP::ECB_Mode::Encryption m_macEnc; - Mutex x_macEnc; - CryptoPP::SHA3_256 m_egressMac; - CryptoPP::SHA3_256 m_ingressMac; + CryptoPP::SecByteBlock m_frameEncKey; ///< Key for m_frameEnc + CryptoPP::CTR_Mode::Encryption m_frameEnc; ///< Encoder for egress plaintext. + + CryptoPP::SecByteBlock m_frameDecKey; ///< Key for m_frameDec + CryptoPP::CTR_Mode::Encryption m_frameDec; ///< Decoder for egress plaintext. + + CryptoPP::SecByteBlock m_macEncKey; /// Key for m_macEnd + CryptoPP::ECB_Mode::Encryption m_macEnc; /// One-way coder used by updateMAC for ingress and egress MAC updates. + Mutex x_macEnc; /// Mutex + + CryptoPP::SHA3_256 m_egressMac; ///< State of MAC for egress ciphertext. + CryptoPP::SHA3_256 m_ingressMac; ///< State of MAC for ingress ciphertext. std::shared_ptr m_socket; }; From 41109e2b6c7111da603a7e6441329b48358bbb66 Mon Sep 17 00:00:00 2001 From: subtly Date: Sat, 7 Mar 2015 22:28:38 +0100 Subject: [PATCH 042/100] docs. ensure non-random memory allocation headermac bytes. --- libp2p/RLPxFrameIO.cpp | 5 ++- libp2p/RLPxFrameIO.h | 6 ++++ libp2p/RLPxHandshake.cpp | 5 +-- libp2p/RLPxHandshake.h | 75 ++++++++++++++++++++++++++-------------- 4 files changed, 60 insertions(+), 31 deletions(-) diff --git a/libp2p/RLPxFrameIO.cpp b/libp2p/RLPxFrameIO.cpp index 2c233c1d4..cb768bfeb 100644 --- a/libp2p/RLPxFrameIO.cpp +++ b/libp2p/RLPxFrameIO.cpp @@ -104,9 +104,8 @@ void RLPXFrameIO::writeSingleFramePacket(bytesConstRef _packet, bytes& o_bytes) // TODO: SECURITY check that header is <= 16 bytes - bytes headerWithMac; - header.swapOut(headerWithMac); - headerWithMac.resize(32); + bytes headerWithMac(h256::size); + bytesConstRef(&header.out()).copyTo(bytesRef(&headerWithMac)); m_frameEnc.ProcessData(headerWithMac.data(), headerWithMac.data(), 16); updateEgressMACWithHeader(bytesConstRef(&headerWithMac).cropped(0, 16)); egressDigest().ref().copyTo(bytesRef(&headerWithMac).cropped(h128::size,h128::size)); diff --git a/libp2p/RLPxFrameIO.h b/libp2p/RLPxFrameIO.h index 3a0fd35e0..0f0504e48 100644 --- a/libp2p/RLPxFrameIO.h +++ b/libp2p/RLPxFrameIO.h @@ -41,6 +41,12 @@ class RLPXHandshake; /** * @brief Encoder/decoder transport for RLPx connections established by RLPXHandshake. * Managed (via shared_ptr) socket for use by RLPXHandshake and RLPXFrameIO. + * + * Thread Safety + * Distinct Objects: Safe. + * Shared objects: Unsafe. + * * an instance method must not be called concurrently + * * a writeSingleFramePacket can be called concurrent to authAndDecryptHeader OR authAndDecryptFrame */ class RLPXSocket: public std::enable_shared_from_this { diff --git a/libp2p/RLPxHandshake.cpp b/libp2p/RLPxHandshake.cpp index a1ede6056..801848107 100644 --- a/libp2p/RLPxHandshake.cpp +++ b/libp2p/RLPxHandshake.cpp @@ -133,6 +133,8 @@ void RLPXHandshake::error() { clog(NetConnect) << "Disconnecting " << m_socket->remoteEndpoint() << " (Handshake Failed)"; m_socket->close(); + if (m_io != nullptr) + delete m_io; } void RLPXHandshake::transition(boost::system::error_code _ech) @@ -175,7 +177,7 @@ void RLPXHandshake::transition(boost::system::error_code _ech) << m_host->protocolVersion() << m_host->m_clientVersion << m_host->caps() - << m_host->m_tcpPublic.port() + << m_host->listenPort() << m_host->id(); bytes packet; s.swapOut(packet); @@ -252,7 +254,6 @@ void RLPXHandshake::transition(boost::system::error_code _ech) } clog(NetNote) << (m_originated ? "p2p.connect.egress" : "p2p.connect.ingress") << "hello frame: success. starting session."; - RLP rlp(frame.cropped(1)); m_host->startPeerSession(m_remote, rlp, m_io, m_socket->remoteEndpoint()); } diff --git a/libp2p/RLPxHandshake.h b/libp2p/RLPxHandshake.h index cd5e7fb83..7ea95173f 100644 --- a/libp2p/RLPxHandshake.h +++ b/libp2p/RLPxHandshake.h @@ -36,66 +36,89 @@ namespace dev namespace p2p { +/** + * @brief Setup inbound or outbound connection for communication over RLPXFrameIO. + * RLPx Spec: https://github.com/ethereum/devp2p/blob/master/rlpx.md#encrypted-handshake + * + * @todo Implement StartSession transition via lambda which is passed to constructor. + * + * Thread Safety + * Distinct Objects: Safe. + * Shared objects: Unsafe. + */ class RLPXHandshake: public std::enable_shared_from_this { friend class RLPXFrameIO; + + /// Sequential states of handshake enum State { Error = -1, - New, // New->AckAuth [egress: tx auth, ingress: rx auth] - AckAuth, // AckAuth->WriteHello [egress: rx ack, ingress: tx ack] - WriteHello, // WriteHello [tx caps, rx caps, writehello] + New, + AckAuth, + WriteHello, ReadHello, StartSession }; public: - /// Handshake for ingress connection. Takes ownership of socket. + /// Setup incoming connection. RLPXHandshake(Host* _host, std::shared_ptr const& _socket): m_host(_host), m_originated(false), m_socket(_socket) { crypto::Nonce::get().ref().copyTo(m_nonce.ref()); } - /// Handshake for egress connection to _remote. Takes ownership of socket. + /// Setup outbound connection. RLPXHandshake(Host* _host, std::shared_ptr const& _socket, NodeId _remote): m_host(_host), m_remote(_remote), m_originated(true), m_socket(_socket) { crypto::Nonce::get().ref().copyTo(m_nonce.ref()); } ~RLPXHandshake() {} + /// Start handshake. void start() { transition(); } protected: + /// Write Auth message to socket and transitions to AckAuth. void writeAuth(); + + /// Reads Auth message from socket and transitions to AckAuth. void readAuth(); + /// Write Ack message to socket and transitions to WriteHello. void writeAck(); + + /// Reads Auth message from socket and transitions to WriteHello. void readAck(); + /// Closes connection and ends transitions. void error(); + + /// Performs transition for m_nextState. void transition(boost::system::error_code _ech = boost::system::error_code()); - /// Current state of handshake. - State m_nextState = New; + State m_nextState = New; ///< Current or expected state of transition. - Host* m_host; + Host* m_host; ///< Host which provides m_alias, protocolVersion(), m_clientVersion, caps(), and TCP listenPort(). /// Node id of remote host for socket. - NodeId m_remote; - bool m_originated = false; + NodeId m_remote; ///< Public address of remote host. + bool m_originated = false; ///< True if connection is outbound. /// Buffers for encoded and decoded handshake phases - bytes m_auth; - bytes m_authCipher; - bytes m_ack; - bytes m_ackCipher; - bytes m_handshakeOutBuffer; - bytes m_handshakeInBuffer; - - crypto::ECDHE m_ecdhe; - h256 m_nonce; - - Public m_remoteEphemeral; - h256 m_remoteNonce; - - /// Frame IO is used to read frame for last step of handshake authentication. - RLPXFrameIO* m_io; - std::shared_ptr m_socket; + bytes m_auth; ///< Plaintext of egress or ingress Auth message. + bytes m_authCipher; ///< Ciphertext of egress or ingress Auth message. + bytes m_ack; ///< Plaintext of egress or ingress Ack message. + bytes m_ackCipher; ///< Ciphertext of egress or ingress Ack message. + bytes m_handshakeOutBuffer; ///< Frame buffer for egress Hello packet. + bytes m_handshakeInBuffer; ///< Frame buffer for ingress Hello packet. + + crypto::ECDHE m_ecdhe; ///< Ephemeral ECDH secret and agreement. + h256 m_nonce; ///< Nonce generated by this host for handshake. + + Public m_remoteEphemeral; ///< Remote ephemeral public key. + h256 m_remoteNonce; ///< Nonce generated by remote host for handshake. + + /// Used to read and write RLPx encrypted frames for last step of handshake authentication. + /// Passed onto Host which will take ownership. + RLPXFrameIO* m_io = nullptr; + + std::shared_ptr m_socket; ///< Socket. }; } From fada98b8d967d0739b7f39709ff6440ce5eb9d8c Mon Sep 17 00:00:00 2001 From: subtly Date: Sat, 7 Mar 2015 22:40:15 +0100 Subject: [PATCH 043/100] update headers, docs, remove unused length argument from fixed-length read --- libdevcrypto/Common.h | 10 +++++----- libdevcrypto/CryptoPP.h | 10 +++++----- libp2p/RLPxHandshake.cpp | 4 ++-- libp2p/RLPxHandshake.h | 1 - libp2p/Session.h | 3 +-- 5 files changed, 13 insertions(+), 15 deletions(-) diff --git a/libdevcrypto/Common.h b/libdevcrypto/Common.h index 1bc16fa93..38e5649fb 100644 --- a/libdevcrypto/Common.h +++ b/libdevcrypto/Common.h @@ -96,19 +96,19 @@ void encryptSym(Secret const& _k, bytesConstRef _plain, bytes& o_cipher); /// Symmetric decryption. bool decryptSym(Secret const& _k, bytesConstRef _cipher, bytes& o_plaintext); -/// Encrypt payload using ECIES standard with AES-CTR. TODO: move into class. +/// Encrypt payload using ECIES standard with AES128-CTR. void encryptECIES(Public const& _k, bytesConstRef _plain, bytes& o_cipher); -/// Decrypt payload using ECIES standard with AES-CTR. TODO: move into class. +/// Decrypt payload using ECIES standard with AES128-CTR. bool decryptECIES(Secret const& _k, bytesConstRef _cipher, bytes& o_plaintext); -/// Encrypts payload with random IV using AES-CTR. TODO: prefix IV. +/// Encrypts payload with random IV/ctr using AES128-CTR. h128 encryptSymNoAuth(Secret const& _k, bytesConstRef _plain, bytes& o_cipher); -/// Encrypts payload with specified IV using AES-CTR TODO: prefix IV. +/// Encrypts payload with specified IV/ctr using AES128-CTR. h128 encryptSymNoAuth(Secret const& _k, bytesConstRef _plain, bytes& o_cipher, h128 const& _iv); -/// Decrypts payload with specified IV TODO: prefix IV. +/// Decrypts payload with specified IV/ctr. bool decryptSymNoAuth(Secret const& _k, h128 const& _iv, bytesConstRef _cipher, bytes& o_plaintext); /// Recovers Public key from signed message hash. diff --git a/libdevcrypto/CryptoPP.h b/libdevcrypto/CryptoPP.h index e0f5f16ba..b673c44a6 100644 --- a/libdevcrypto/CryptoPP.h +++ b/libdevcrypto/CryptoPP.h @@ -76,19 +76,19 @@ public: void toPublic(Secret const& _s, Public& o_public) { exponentToPublic(Integer(_s.data(), sizeof(_s)), o_public); } - /// Encrypts text (replace input). + /// Encrypts text (replace input). (ECIES w/XOR) void encrypt(Public const& _k, bytes& io_cipher); - /// Decrypts text (replace input). + /// Decrypts text (replace input). (ECIES w/XOR) void decrypt(Secret const& _k, bytes& io_text); - /// Temporary; to replace encrypt once interop w/go is passing. + /// Encrypts text (replace input). (ECIES w/AES128-CTR) void encryptECIES(Public const& _k, bytes& io_cipher); - /// Temporary; to replace decrypt once interop w/go is passing. + /// Decrypts text (replace input). (ECIES w/AES128-CTR) bool decryptECIES(Secret const& _k, bytes& io_text); - /// Key derivation function used by ECIES. + /// Key derivation function used by encryptECIES and decryptECIES. bytes eciesKDF(Secret _z, bytes _s1, unsigned kdBitLen = 256); /// @returns siganture of message. diff --git a/libp2p/RLPxHandshake.cpp b/libp2p/RLPxHandshake.cpp index 801848107..fa346ec09 100644 --- a/libp2p/RLPxHandshake.cpp +++ b/libp2p/RLPxHandshake.cpp @@ -195,7 +195,7 @@ void RLPXHandshake::transition(boost::system::error_code _ech) // read frame header m_handshakeInBuffer.resize(h256::size); - ba::async_read(m_socket->ref(), boost::asio::buffer(m_handshakeInBuffer, h256::size), [this, self](boost::system::error_code ec, std::size_t length) + ba::async_read(m_socket->ref(), boost::asio::buffer(m_handshakeInBuffer, h256::size), [this, self](boost::system::error_code ec, std::size_t) { if (ec) transition(ec); @@ -229,7 +229,7 @@ void RLPXHandshake::transition(boost::system::error_code _ech) /// read padded frame and mac m_handshakeInBuffer.resize(frameSize + ((16 - (frameSize % 16)) % 16) + h128::size); - ba::async_read(m_socket->ref(), boost::asio::buffer(m_handshakeInBuffer, m_handshakeInBuffer.size()), [this, self, headerRLP](boost::system::error_code ec, std::size_t length) + ba::async_read(m_socket->ref(), boost::asio::buffer(m_handshakeInBuffer, m_handshakeInBuffer.size()), [this, self, headerRLP](boost::system::error_code ec, std::size_t) { if (ec) transition(ec); diff --git a/libp2p/RLPxHandshake.h b/libp2p/RLPxHandshake.h index 7ea95173f..8459de053 100644 --- a/libp2p/RLPxHandshake.h +++ b/libp2p/RLPxHandshake.h @@ -25,7 +25,6 @@ #include #include #include -#include #include "RLPxFrameIO.h" #include "Common.h" namespace ba = boost::asio; diff --git a/libp2p/Session.h b/libp2p/Session.h index 77b7de7bb..51db5adc3 100644 --- a/libp2p/Session.h +++ b/libp2p/Session.h @@ -85,8 +85,7 @@ public: private: void send(bytes&& _msg); -// void send(bytesConstRef _msg); - + /// Drop the connection for the reason @a _r. void drop(DisconnectReason _r); From d430ac7bef46688f98554be309a7cba078ff2e4b Mon Sep 17 00:00:00 2001 From: CJentzsch Date: Sat, 7 Mar 2015 23:52:26 +0100 Subject: [PATCH 044/100] adjust gas prices for arithmetic tests --- test/vmArithmeticTestFiller.json | 404 ++++++++++++---------- test/vmIOandFlowOperationsTestFiller.json | 225 +++++++++++- 2 files changed, 440 insertions(+), 189 deletions(-) diff --git a/test/vmArithmeticTestFiller.json b/test/vmArithmeticTestFiller.json index 933df72d1..71d274175 100644 --- a/test/vmArithmeticTestFiller.json +++ b/test/vmArithmeticTestFiller.json @@ -22,7 +22,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -50,7 +50,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -78,7 +78,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -106,7 +106,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -134,7 +134,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -162,7 +162,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -191,7 +191,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -220,7 +220,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -248,7 +248,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -276,7 +276,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -304,7 +304,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -332,7 +332,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -360,7 +360,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -388,7 +388,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -416,7 +416,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -444,7 +444,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -472,7 +472,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -500,7 +500,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -528,7 +528,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -556,7 +556,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -584,7 +584,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -612,7 +612,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -640,7 +640,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -668,7 +668,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -696,7 +696,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -724,7 +724,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -752,7 +752,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -780,7 +780,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -808,7 +808,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -836,7 +836,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -864,7 +864,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -892,7 +892,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -920,7 +920,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -948,8 +948,8 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", - "gas" : "10000" + "gasPrice" : "1", + "gas" : "1000000" } }, @@ -976,8 +976,36 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", - "gas" : "10000" + "gasPrice" : "1", + "gas" : "1000000" + } + }, + + "sdiv_dejavu": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : "1", + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : "0", + "code" : "{ asm PUSH1 0x05 PUSH1 0x09 PUSH1 0x00 SUB SDIV DUP PUSH1 0 SSTORE }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f2947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "1", + "gas" : "10000000" } }, @@ -1004,7 +1032,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -1032,7 +1060,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -1060,7 +1088,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -1088,7 +1116,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -1116,7 +1144,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -1144,7 +1172,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -1172,7 +1200,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -1200,7 +1228,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -1228,7 +1256,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -1256,7 +1284,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -1284,8 +1312,8 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", - "gas" : "10000" + "gasPrice" : "1", + "gas" : "1000000" } }, @@ -1312,8 +1340,8 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", - "gas" : "10000" + "gasPrice" : "1", + "gas" : "1000000" } }, @@ -1340,8 +1368,8 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", - "gas" : "10000" + "gasPrice" : "1", + "gas" : "1000000" } }, @@ -1368,7 +1396,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -1396,7 +1424,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -1424,8 +1452,8 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", - "gas" : "10000" + "gasPrice" : "1", + "gas" : "1000000" } }, @@ -1452,8 +1480,8 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", - "gas" : "10000" + "gasPrice" : "1", + "gas" : "1000000" } }, @@ -1480,8 +1508,8 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", - "gas" : "10000" + "gasPrice" : "1", + "gas" : "1000000" } }, @@ -1508,8 +1536,8 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", - "gas" : "10000" + "gasPrice" : "1", + "gas" : "1000000" } }, @@ -1536,7 +1564,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -1564,7 +1592,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -1592,7 +1620,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -1620,7 +1648,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -1648,7 +1676,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -1676,7 +1704,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -1704,7 +1732,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -1732,7 +1760,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -1761,7 +1789,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -1789,7 +1817,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -1817,8 +1845,8 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", - "gas" : "10000" + "gasPrice" : "1", + "gas" : "1000000" } }, @@ -1845,8 +1873,8 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", - "gas" : "10000" + "gasPrice" : "1", + "gas" : "1000000" } }, @@ -1873,8 +1901,8 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", - "gas" : "10000" + "gasPrice" : "1", + "gas" : "1000000" } }, @@ -1901,8 +1929,8 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", - "gas" : "10000" + "gasPrice" : "1", + "gas" : "1000000" } }, @@ -1929,7 +1957,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -1957,7 +1985,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -1985,7 +2013,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -2013,7 +2041,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -2041,7 +2069,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -2069,7 +2097,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -2097,7 +2125,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -2125,7 +2153,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -2153,7 +2181,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -2182,7 +2210,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -2210,7 +2238,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -2238,7 +2266,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -2266,7 +2294,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -2294,7 +2322,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -2322,7 +2350,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -2350,7 +2378,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -2378,7 +2406,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -2406,7 +2434,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -2434,7 +2462,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -2462,7 +2490,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -2490,7 +2518,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -2518,7 +2546,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -2546,7 +2574,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -2574,7 +2602,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -2602,7 +2630,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -2630,7 +2658,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -2658,7 +2686,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -2686,7 +2714,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -2714,7 +2742,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -2742,7 +2770,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -2770,7 +2798,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -2798,7 +2826,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -2826,7 +2854,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -2854,7 +2882,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -2882,7 +2910,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -2910,7 +2938,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -2938,7 +2966,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -2966,7 +2994,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -2994,7 +3022,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -3022,7 +3050,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -3050,7 +3078,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -3078,7 +3106,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -3106,7 +3134,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -3134,7 +3162,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -3162,7 +3190,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -3190,7 +3218,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -3218,7 +3246,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -3246,7 +3274,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -3274,7 +3302,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -3302,7 +3330,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -3330,7 +3358,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -3358,7 +3386,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -3386,7 +3414,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -3414,7 +3442,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -3442,7 +3470,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -3470,7 +3498,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -3498,7 +3526,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -3526,7 +3554,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -3554,7 +3582,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -3582,7 +3610,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -3610,7 +3638,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -3638,7 +3666,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -3666,7 +3694,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -3694,7 +3722,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -3722,7 +3750,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -3750,7 +3778,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -3778,7 +3806,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -3806,7 +3834,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -3834,7 +3862,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -3862,7 +3890,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -3890,7 +3918,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -3918,7 +3946,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -3946,7 +3974,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -3974,7 +4002,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -4002,7 +4030,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -4030,7 +4058,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -4058,7 +4086,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -4086,7 +4114,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -4114,7 +4142,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -4142,7 +4170,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -4170,7 +4198,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -4198,7 +4226,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -4226,7 +4254,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -4254,7 +4282,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -4282,7 +4310,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -4310,7 +4338,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -4338,7 +4366,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -4366,7 +4394,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -4394,7 +4422,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -4422,7 +4450,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -4450,7 +4478,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -4478,7 +4506,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -4506,7 +4534,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -4534,7 +4562,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -4562,7 +4590,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -4590,7 +4618,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -4618,7 +4646,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -4646,7 +4674,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -4674,7 +4702,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -4702,7 +4730,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -4730,7 +4758,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -4758,7 +4786,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -4786,7 +4814,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -4814,7 +4842,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -4842,7 +4870,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -4870,7 +4898,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } }, @@ -4898,7 +4926,7 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", + "gasPrice" : "1", "gas" : "100000" } } diff --git a/test/vmIOandFlowOperationsTestFiller.json b/test/vmIOandFlowOperationsTestFiller.json index 995b9d6da..84a5188a6 100644 --- a/test/vmIOandFlowOperationsTestFiller.json +++ b/test/vmIOandFlowOperationsTestFiller.json @@ -283,7 +283,7 @@ "env" : { "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", "currentNumber" : "0", - "currentGasLimit" : "1000000", + "currentGasLimit" : "8390000000", "currentDifficulty" : "256", "currentTimestamp" : "1", "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" @@ -306,6 +306,229 @@ "gas" : "100000" } }, + "mstore8MemExp": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "8390000000", + "currentDifficulty" : "256", + "currentTimestamp" : "1", + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "100000000000000000000000", + "nonce" : "0", + "code" : "0x60f1630fffffff53", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "1", + "gas" : "8390000" + } + }, + + "mloadMemExp": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "8390000000", + "currentDifficulty" : "256", + "currentTimestamp" : "1", + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "100000000000000000000000", + "nonce" : "0", + "code" : "0x630fffffff51", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "1", + "gas" : "8390000" + } + }, + + "mstoreMemExp": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "8390000000", + "currentDifficulty" : "256", + "currentTimestamp" : "1", + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "100000000000000000000000", + "nonce" : "0", + "code" : "0x60f1630fffffff52", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "1", + "gas" : "8390000000" + } + }, + + "log1MemExp": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "8390000000", + "currentDifficulty" : "256", + "currentTimestamp" : "1", + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "100000000000000000000000", + "nonce" : "0", + "code" : "0x60ff60ff630fffffffa1", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "1", + "gas" : "8390000000" + } + }, + + "extcodecopyMemExp": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "8390000000", + "currentDifficulty" : "256", + "currentTimestamp" : "1", + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "100000000000000000000000", + "nonce" : "0", + "code" : "0x60ff60ff630fffffff630fffffff3c", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "1", + "gas" : "8390000000" + } + }, + + "codecopyMemExp": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "8390000000", + "currentDifficulty" : "256", + "currentTimestamp" : "1", + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "100000000000000000000000", + "nonce" : "0", + "code" : "0x60ff60ff630fffffff630fffffff39", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "1", + "gas" : "8390000000" + } + }, + + "calldatacopyMemExp": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "8390000000", + "currentDifficulty" : "256", + "currentTimestamp" : "1", + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "100000000000000000000000", + "nonce" : "0", + "code" : "0x60ff60ff630fffffff630fffffff37", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "1", + "gas" : "8390000000" + } + }, + + "sha3MemExp": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "8390000000", + "currentDifficulty" : "256", + "currentTimestamp" : "1", + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "100000000000000000000000", + "nonce" : "0", + "code" : "0x60ff630fffffff20", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "1", + "gas" : "8390000000" + } + }, "mstore8WordToBigError": { "env" : { From 6b5c3acf267a803a7c483f6702d0717817985163 Mon Sep 17 00:00:00 2001 From: subtly Date: Sun, 8 Mar 2015 13:45:23 +0100 Subject: [PATCH 045/100] fix kdf test --- test/crypto.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/crypto.cpp b/test/crypto.cpp index c20b4c51f..a844cbf09 100644 --- a/test/crypto.cpp +++ b/test/crypto.cpp @@ -267,14 +267,14 @@ BOOST_AUTO_TEST_CASE(ecies_kdf) // nonce Secret z1; ecdh::agree(local.sec(), remote.pub(), z1); - auto key1 = s_secp256k1.eciesKDF(z1, bytes(), 512); + auto key1 = s_secp256k1.eciesKDF(z1, bytes(), 64); bytesConstRef eKey1 = bytesConstRef(&key1).cropped(0, 32); bytesRef mKey1 = bytesRef(&key1).cropped(32, 32); sha3(mKey1, mKey1); Secret z2; ecdh::agree(remote.sec(), local.pub(), z2); - auto key2 = s_secp256k1.eciesKDF(z2, bytes(), 512); + auto key2 = s_secp256k1.eciesKDF(z2, bytes(), 64); bytesConstRef eKey2 = bytesConstRef(&key2).cropped(0, 32); bytesRef mKey2 = bytesRef(&key2).cropped(32, 32); sha3(mKey2, mKey2); From d78da4ca8fd72f0f96ab0109a7b2ab8e8b4bd6df Mon Sep 17 00:00:00 2001 From: subtly Date: Sun, 8 Mar 2015 13:48:49 +0100 Subject: [PATCH 046/100] coding standards --- libdevcrypto/Common.cpp | 8 ++++---- test/crypto.cpp | 12 ++++++------ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/libdevcrypto/Common.cpp b/libdevcrypto/Common.cpp index 4abac7eed..e108b230f 100644 --- a/libdevcrypto/Common.cpp +++ b/libdevcrypto/Common.cpp @@ -129,9 +129,9 @@ h128 dev::encryptSymNoAuth(Secret const& _k, bytesConstRef _plain, bytes& o_ciph e.ProcessData(o_cipher.data(), _plain.data(), _plain.size()); return _iv; } - catch(CryptoPP::Exception& e) + catch (CryptoPP::Exception& _e) { - cerr << e.what() << endl; + cerr << _e.what() << endl; o_cipher.resize(0); return h128(); } @@ -150,9 +150,9 @@ bool dev::decryptSymNoAuth(Secret const& _k, h128 const& _iv, bytesConstRef _cip d.ProcessData(o_plaintext.data(), _cipher.data(), _cipher.size()); return true; } - catch(CryptoPP::Exception& e) + catch (CryptoPP::Exception& _e) { - cerr << e.what() << endl; + cerr << _e.what() << endl; o_plaintext.resize(0); return false; } diff --git a/test/crypto.cpp b/test/crypto.cpp index a844cbf09..2d7121f89 100644 --- a/test/crypto.cpp +++ b/test/crypto.cpp @@ -658,9 +658,9 @@ BOOST_AUTO_TEST_CASE(cryptopp_aes128_ctr) BOOST_REQUIRE(text != original); cipherCopy = text; } - catch(CryptoPP::Exception& e) + catch (CryptoPP::Exception& _e) { - cerr << e.what() << endl; + cerr << _e.what() << endl; } try @@ -670,9 +670,9 @@ BOOST_AUTO_TEST_CASE(cryptopp_aes128_ctr) d.ProcessData(out, in, text.size()); BOOST_REQUIRE(text == original); } - catch(CryptoPP::Exception& e) + catch (CryptoPP::Exception& _e) { - cerr << e.what() << endl; + cerr << _e.what() << endl; } @@ -690,9 +690,9 @@ BOOST_AUTO_TEST_CASE(cryptopp_aes128_ctr) // yep, ctr mode. BOOST_REQUIRE(cipherCopy == original); } - catch(CryptoPP::Exception& e) + catch (CryptoPP::Exception& _e) { - cerr << e.what() << endl; + cerr << _e.what() << endl; } } From 529fda3a957aecb7f4ece6307225b7b5cfd89067 Mon Sep 17 00:00:00 2001 From: subtly Date: Mon, 9 Mar 2015 12:51:24 +0100 Subject: [PATCH 047/100] lifecycle management of handshake sessions --- libp2p/Host.cpp | 86 ++++++++++++++++++++++------------------ libp2p/Host.h | 5 ++- libp2p/RLPxHandshake.cpp | 2 + libp2p/RLPxHandshake.h | 2 + 4 files changed, 56 insertions(+), 39 deletions(-) diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index 5377d80b8..e8e206233 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -121,6 +121,23 @@ void Host::doneWorking() for (auto const& h: m_capabilities) h.second->onStopping(); + // disconnect pending handshake, before peers, as a handshake may create a peer + for (unsigned n = 0;; n = 0) + { + { + Guard l(x_connecting); + for (auto i: m_connecting) + if (auto h = i.lock()) + { + h->cancel(); + n++; + } + } + if (!n) + break; + m_ioService.poll(); + } + // disconnect peers for (unsigned n = 0;; n = 0) { @@ -157,7 +174,7 @@ unsigned Host::protocolVersion() const return 3; } -bool Host::startPeerSession(Public const& _id, RLP const& _rlp, RLPXFrameIO* _io, bi::tcp::endpoint _endpoint) +void Host::startPeerSession(Public const& _id, RLP const& _rlp, RLPXFrameIO* _io, bi::tcp::endpoint _endpoint) { /// Get or create Peer shared_ptr p; @@ -171,7 +188,6 @@ bool Host::startPeerSession(Public const& _id, RLP const& _rlp, RLPXFrameIO* _io if (p->isOffline()) p->m_lastConnected = std::chrono::system_clock::now(); p->m_failedAttempts = 0; - // TODO: update pendingconns w/session-weak-ptr for graceful shutdown (otherwise this line isn't safe) p->endpoint.tcp.address(_endpoint.address()); auto protocolVersion = _rlp[0].toInt(); @@ -191,18 +207,20 @@ bool Host::startPeerSession(Public const& _id, RLP const& _rlp, RLPXFrameIO* _io if (protocolVersion != this->protocolVersion()) { ps->disconnect(IncompatibleProtocol); - return false; + return; } { RecursiveGuard l(x_sessions); if (m_sessions.count(_id) && !!m_sessions[_id].lock()) - { - // Already connected. - clog(NetWarn) << "Session already exists for peer with id" << _id.abridged(); - ps->disconnect(DuplicatePeer); - return false; - } + if (auto s = m_sessions[_id].lock()) + if(s->isConnected()) + { + // Already connected. + clog(NetWarn) << "Session already exists for peer with id" << _id.abridged(); + ps->disconnect(DuplicatePeer); + return; + } m_sessions[_id] = ps; } ps->start(); @@ -215,7 +233,6 @@ bool Host::startPeerSession(Public const& _id, RLP const& _rlp, RLPXFrameIO* _io } clog(NetNote) << "p2p.host.peer.register" << _id.abridged(); StructuredLogger::p2pConnected(_id.abridged(), ps->m_peer->peerEndpoint(), ps->m_peer->m_lastConnected, clientVersion, peerCount()); - return true; } void Host::onNodeTableEvent(NodeId const& _n, NodeTableEventType const& _e) @@ -340,22 +357,6 @@ void Host::runAcceptor() clog(NetConnect) << "Listening on local port " << m_listenPort << " (public: " << m_tcpPublic << ")"; m_accepting = true; - // socket is created outside of acceptor-callback - // An allocated socket is necessary as asio can use the socket - // until the callback succeeds or fails. - // - // Until callback succeeds or fails, we can't dealloc it. - // - // Callback is guaranteed to be called via asio or when - // m_tcp4Acceptor->stop() is called by Host. - // - // All exceptions are caught so they don't halt asio and so the - // socket is deleted. - // - // It's possible for an accepted connection to return an error in which - // case the socket may be open and must be closed to prevent asio from - // processing socket events after socket is deallocated. - auto socket = make_shared(new bi::tcp::socket(m_ioService)); m_tcp4Acceptor.async_accept(socket->ref(), [=](boost::system::error_code ec) { @@ -367,6 +368,7 @@ void Host::runAcceptor() { // incoming connection; we don't yet know nodeid auto handshake = make_shared(this, socket); + m_connecting.push_back(handshake); handshake->start(); success = true; } @@ -435,15 +437,14 @@ void Host::addNode(NodeId const& _node, std::string const& _addr, unsigned short void Host::connect(std::shared_ptr const& _p) { - for (unsigned i = 0; i < 200; i++) - if (isWorking() && !m_run) - this_thread::sleep_for(chrono::milliseconds(50)); if (!m_run) return; + _p->m_lastAttempted = std::chrono::system_clock::now(); + if (havePeerSession(_p->id)) { - clog(NetWarn) << "Aborted connect. Node already connected."; + clog(NetConnect) << "Aborted connect. Node already connected."; return; } @@ -475,10 +476,15 @@ void Host::connect(std::shared_ptr const& _p) } else { - clog(NetConnect) << "Connected to" << _p->id.abridged() << "@" << _p->peerEndpoint(); + clog(NetConnect) << "Connecting to" << _p->id.abridged() << "@" << _p->peerEndpoint(); auto handshake = make_shared(this, socket, _p->id); + { + Guard l(x_connecting); + m_connecting.push_back(handshake); + } handshake->start(); } + Guard l(x_pendingNodeConns); m_pendingPeerConns.erase(nptr); }); @@ -527,26 +533,30 @@ void Host::run(boost::system::error_code const&) m_nodeTable->processEvents(); + // cleanup zombies + { + Guard l(x_connecting); + m_connecting.remove_if([](std::weak_ptr h){ return h.lock(); }); + } + for (auto p: m_sessions) if (auto pp = p.second.lock()) pp->serviceNodesRequest(); -// keepAlivePeers(); + keepAlivePeers(); // disconnectLatePeers(); - auto c = peerCount(); - if (m_idealPeerCount && !c) + if (peerCount() < m_idealPeerCount) + { for (auto p: m_peers) if (p.second->shouldReconnect()) { - // TODO p2p: fixme - p.second->m_lastAttempted = std::chrono::system_clock::now(); connect(p.second); break; } - - if (c < m_idealPeerCount) + m_nodeTable->discover(); + } auto runcb = [this](boost::system::error_code const& error) { run(error); }; m_timer->expires_from_now(boost::posix_time::milliseconds(c_timerInterval)); diff --git a/libp2p/Host.h b/libp2p/Host.h index 00fcc0943..ccb34b064 100644 --- a/libp2p/Host.h +++ b/libp2p/Host.h @@ -151,7 +151,7 @@ public: NodeId id() const { return m_alias.pub(); } /// Validates and starts peer session, taking ownership of _io. Disconnects and returns false upon error. - bool startPeerSession(Public const& _id, RLP const& _hello, RLPXFrameIO* _io, bi::tcp::endpoint _endpoint); + void startPeerSession(Public const& _id, RLP const& _hello, RLPXFrameIO* _io, bi::tcp::endpoint _endpoint); protected: void onNodeTableEvent(NodeId const& _n, NodeTableEventType const& _e); @@ -224,6 +224,9 @@ private: /// Mutable because we flush zombie entries (null-weakptrs) as regular maintenance from a const method. mutable std::map> m_sessions; mutable RecursiveMutex x_sessions; + + std::list> m_connecting; ///< Pending connections. + Mutex x_connecting; unsigned m_idealPeerCount = 5; ///< Ideal number of peers to be connected to. diff --git a/libp2p/RLPxHandshake.cpp b/libp2p/RLPxHandshake.cpp index fa346ec09..4f5c70802 100644 --- a/libp2p/RLPxHandshake.cpp +++ b/libp2p/RLPxHandshake.cpp @@ -168,6 +168,8 @@ void RLPXHandshake::transition(boost::system::error_code _ech) else clog(NetConnect) << "p2p.connect.ingress sending capabilities handshake"; + /// This pointer will be freed if there is an error otherwise + /// it will be passed to Host which will take ownership. m_io = new RLPXFrameIO(*this); // old packet format diff --git a/libp2p/RLPxHandshake.h b/libp2p/RLPxHandshake.h index 8459de053..aac8f4b5a 100644 --- a/libp2p/RLPxHandshake.h +++ b/libp2p/RLPxHandshake.h @@ -72,6 +72,8 @@ public: /// Start handshake. void start() { transition(); } + void cancel() { m_nextState = Error; } + protected: /// Write Auth message to socket and transitions to AckAuth. void writeAuth(); From c22df42b0b44830011bce256faf62eb824c5d63d Mon Sep 17 00:00:00 2001 From: yann300 Date: Mon, 9 Mar 2015 13:11:02 +0100 Subject: [PATCH 048/100] - use input to filter logs by any str. - format exceptions coming from Executive.cpp --- mix/qml/LogsPane.qml | 66 +++++++++++++++++++++++++----------- mix/qml/StatusPane.qml | 23 +++++++++++-- mix/sortfilterproxymodel.cpp | 64 +++++++++++++++++++++++++++++----- mix/sortfilterproxymodel.h | 18 ++++++++-- 4 files changed, 138 insertions(+), 33 deletions(-) diff --git a/mix/qml/LogsPane.qml b/mix/qml/LogsPane.qml index 9fa15f250..ca78b3854 100644 --- a/mix/qml/LogsPane.qml +++ b/mix/qml/LogsPane.qml @@ -12,6 +12,7 @@ Rectangle _content = _content.replace(/\n/g, " ") logsModel.insert(0, { "type": _type, "date": Qt.formatDateTime(new Date(), "hh:mm:ss dd.MM.yyyy"), "content": _content, "level": _level }); } + anchors.fill: parent radius: 5 color: "#f7f7f7" @@ -150,10 +151,24 @@ Rectangle } } } + + DefaultTextField + { + id: searchBox + height: 30 + anchors.verticalCenter: parent.verticalCenter + width: 200 + font.family: "sans serif" + font.pointSize: Style.absoluteSize(-3) + onTextChanged: { + proxyModel.search(text); + } + } } ListModel { id: logsModel + } TableView { @@ -171,17 +186,41 @@ Rectangle model: SortFilterProxyModel { id: proxyModel source: logsModel + property var roles: ["-", "javascript", "run", "state"] + + Component.onCompleted: { + filterType = regEx(proxyModel.roles); + } + + function search(_value) + { + filterContent = _value; + } function toogleFilter(_value) { - if (filterString.indexOf('_' + _value) !== -1) - filterString = filterString.replace('_' + _value, _value); - else - filterString = filterString.replace(_value, '_' + _value); + var count = roles.length; + for (var i in roles) + { + if (roles[i] === _value) + { + roles.splice(i, 1); + break; + } + } + if (count === roles.length) + roles.push(_value); + + filterType = regEx(proxyModel.roles); } - filterRole: "type" - filterString: "(?:javascript|run|state)" + function regEx(_value) + { + console.log("(?:" + roles.join('|') + ")"); + return "(?:" + roles.join('|') + ")"; + } + filterType: "(?:javascript|run|state)" + filterContent: "" filterSyntax: SortFilterProxyModel.RegExp filterCaseSensitivity: Qt.CaseInsensitive } @@ -214,20 +253,7 @@ Rectangle text: styleData.value; font.family: "sans serif" font.pointSize: Style.absoluteSize(-1) - color: { - if (styleData.row > -1) - { - var l = logsModel.get(styleData.row).level - if (l === "error") - return "red" - else if (l === "warning") - return "orange" - else if (l === "info") - return "#808080" - } - else - return "#808080" - } + color: "#808080" } } } diff --git a/mix/qml/StatusPane.qml b/mix/qml/StatusPane.qml index 141d45a48..2a2eeb910 100644 --- a/mix/qml/StatusPane.qml +++ b/mix/qml/StatusPane.qml @@ -59,9 +59,28 @@ Rectangle { Connections { target:clientModel onRunStarted: infoMessage(qsTr("Running transactions..."), "run"); - onRunFailed: errorMessage(qsTr("Error running transactions: " + _message), "run"); + onRunFailed: errorMessage(format(_message), "run"); onRunComplete: infoMessage(qsTr("Run complete"), "run"); onNewBlock: infoMessage(qsTr("New block created"), "state"); + + function format(_message) + { + var formatted = _message.match(/(?:)/); + if (formatted.length > 1) + formatted = formatted[1] + ": "; + var exceptionInfos = _message.match(/(tag_)(.+)/g); + console.log("hh " + exceptionInfos.length); + for (var k in exceptionInfos) + { + formatted += " " + exceptionInfos[k].replace("*]", "").replace("tag_", ""); + console.log(k); + if (k === exceptionInfos.length - 1) + formatted += "." + else + formatted += "," + } + return formatted; + } } Connections { target:projectModel @@ -82,7 +101,7 @@ Rectangle { Rectangle { id: statusContainer anchors.horizontalCenter: parent.horizontalCenter - anchors.verticalCenter: parent.verticalCenter + anchors.verticalCenter: parent.verticalCenterw radius: 3 width: 500 height: 30 diff --git a/mix/sortfilterproxymodel.cpp b/mix/sortfilterproxymodel.cpp index 75a50ea59..04e403738 100644 --- a/mix/sortfilterproxymodel.cpp +++ b/mix/sortfilterproxymodel.cpp @@ -80,15 +80,15 @@ void SortFilterProxyModel::setSortOrder(Qt::SortOrder order) QSortFilterProxyModel::sort(0, order); } -QByteArray SortFilterProxyModel::filterRole() const +/*QByteArray SortFilterProxyModel::filterRole() const { return roleNames().value(QSortFilterProxyModel::filterRole()); -} +}*/ -void SortFilterProxyModel::setFilterRole(const QByteArray &role) +/*void SortFilterProxyModel::setFilterRole(const QByteArray &role) { QSortFilterProxyModel::setFilterRole(roleKey(role)); -} +}*/ QString SortFilterProxyModel::filterString() const { @@ -146,10 +146,11 @@ QHash SortFilterProxyModel::roleNames() const bool SortFilterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const { - QRegExp rx = filterRegExp(); + /*QRegExp rx = filterRegExp(); if (rx.isEmpty()) return true; QAbstractItemModel *model = sourceModel(); + if (filterRole().isEmpty()) { QHash roles = roleNames(); QHashIterator it(roles); @@ -157,14 +158,61 @@ bool SortFilterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &so it.next(); QModelIndex sourceIndex = model->index(sourceRow, 0, sourceParent); QString key = model->data(sourceIndex, it.key()).toString(); - if (key.contains(rx)) + if (key.contains(rx))data return true; } return false; + }*/ + + QRegExp rx = filterRegExp(); + QAbstractItemModel *model = sourceModel(); + QModelIndex sourceIndex = model->index(sourceRow, 0, sourceParent); + if (!sourceIndex.isValid()) + return true; + + QString keyType = model->data(sourceIndex, roleKey(type.toUtf8())).toString(); + QString keyContent = model->data(sourceIndex, roleKey(content.toUtf8())).toString(); + + return keyType.contains(m_filterType) && keyContent.contains(m_filterContent); +/* + for (auto filter: filterRoles()) + { + QString key = model->data(sourceIndex, roleKey(filter.toUtf8())).toString(); + if (!key.contains(rx)) + return false; } + return true; QModelIndex sourceIndex = model->index(sourceRow, 0, sourceParent); if (!sourceIndex.isValid()) return true; - QString key = model->data(sourceIndex, roleKey(filterRole())).toString(); - return key.contains(rx); + for (auto role: filterR) + { + QString key = model->data(sourceIndex, roleKey(role)).toString(); + if (key.contains(rx)) + return true; + } + return false;*/ +} + +void SortFilterProxyModel::setFilterType(const QString &_type) +{ + m_filterType = QRegExp(_type, filterCaseSensitivity(), static_cast(filterSyntax())); + setFilterRegExp(_type); +} + +QString SortFilterProxyModel::filterType() const +{ + return m_filterType.pattern(); } + +void SortFilterProxyModel::setFilterContent(const QString &_content) +{ + m_filterContent = QRegExp(_content, filterCaseSensitivity(), static_cast(filterSyntax())); + setFilterRegExp(_content); +} + +QString SortFilterProxyModel::filterContent() const +{ + return m_filterContent.pattern(); +} + diff --git a/mix/sortfilterproxymodel.h b/mix/sortfilterproxymodel.h index 2ff479413..8eae97a68 100644 --- a/mix/sortfilterproxymodel.h +++ b/mix/sortfilterproxymodel.h @@ -58,7 +58,8 @@ class SortFilterProxyModel : public QSortFilterProxyModel Q_PROPERTY(QByteArray sortRole READ sortRole WRITE setSortRole) Q_PROPERTY(Qt::SortOrder sortOrder READ sortOrder WRITE setSortOrder) - Q_PROPERTY(QByteArray filterRole READ filterRole WRITE setFilterRole) + Q_PROPERTY(QString filterContent READ filterContent WRITE setFilterContent) + Q_PROPERTY(QString filterType READ filterType WRITE setFilterType) Q_PROPERTY(QString filterString READ filterString WRITE setFilterString) Q_PROPERTY(FilterSyntax filterSyntax READ filterSyntax WRITE setFilterSyntax) @@ -75,9 +76,14 @@ public: void setSortOrder(Qt::SortOrder order); - QByteArray filterRole() const; - void setFilterRole(const QByteArray &role); + QString filterContent() const; + void setFilterContent(const QString &_content); + QString filterType() const; + void setFilterType(const QString &_type); + /*QStringList filterRoles() const; + void setFilterRoles(const QStringList &roles); +*/ QString filterString() const; void setFilterString(const QString &filter); @@ -100,6 +106,12 @@ protected: int roleKey(const QByteArray &role) const; QHash roleNames() const; bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const; + +private: + QRegExp m_filterType; + QRegExp m_filterContent; + const QString type = "type"; + const QString content = "content"; }; } From 16812749e5fd580bec1cac3ef5597fd13acfccdb Mon Sep 17 00:00:00 2001 From: yann300 Date: Mon, 9 Mar 2015 13:40:23 +0100 Subject: [PATCH 049/100] format executive.cpp exception --- mix/qml/StatusPane.qml | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/mix/qml/StatusPane.qml b/mix/qml/StatusPane.qml index 2a2eeb910..c17e17f89 100644 --- a/mix/qml/StatusPane.qml +++ b/mix/qml/StatusPane.qml @@ -68,17 +68,11 @@ Rectangle { var formatted = _message.match(/(?:)/); if (formatted.length > 1) formatted = formatted[1] + ": "; - var exceptionInfos = _message.match(/(tag_)(.+)/g); - console.log("hh " + exceptionInfos.length); + else + return _message; + var exceptionInfos = _message.match(/(?:tag_)(.+)/g); for (var k in exceptionInfos) - { - formatted += " " + exceptionInfos[k].replace("*]", "").replace("tag_", ""); - console.log(k); - if (k === exceptionInfos.length - 1) - formatted += "." - else - formatted += "," - } + formatted += " " + exceptionInfos[k].replace("*]", "").replace("tag_", "") + " - "; return formatted; } } From d57e48384b90a77d1b5c41e52f22935e0a073fb1 Mon Sep 17 00:00:00 2001 From: CJentzsch Date: Mon, 9 Mar 2015 14:04:21 +0100 Subject: [PATCH 050/100] uncleBlock reward modification - 15/16 -> (8+uncle.number-block.number)/8 --- libethereum/State.cpp | 16 ++++++++-------- libethereum/State.h | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/libethereum/State.cpp b/libethereum/State.cpp index d1e430dbe..9bfc0c08c 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -555,7 +555,7 @@ u256 State::enact(bytesConstRef _block, BlockChain const& _bc, bool _checkNonce) BOOST_THROW_EXCEPTION(TooManyUncles()); set nonces = { m_currentBlock.nonce }; - Addresses rewarded; + vector rewarded; set knownUncles = _bc.allUnclesFrom(m_currentBlock.parentHash); for (auto const& i: rlp[2]) @@ -574,7 +574,7 @@ u256 State::enact(bytesConstRef _block, BlockChain const& _bc, bool _checkNonce) nonces.insert(uncle.nonce); tdIncrease += uncle.difficulty; - rewarded.push_back(uncle.coinbaseAddress); + rewarded.push_back(uncle); } applyRewards(rewarded); @@ -696,7 +696,7 @@ void State::commitToMine(BlockChain const& _bc) m_lastTx = m_db; - Addresses uncleAddresses; + vector uncleBlockHeaders; RLPStream unclesData; unsigned unclesCount = 0; @@ -716,7 +716,7 @@ void State::commitToMine(BlockChain const& _bc) BlockInfo ubi(_bc.block(u)); ubi.streamRLP(unclesData, WithNonce); ++unclesCount; - uncleAddresses.push_back(ubi.coinbaseAddress); + uncleBlockHeaders.push_back(ubi); if (unclesCount == 2) break; } @@ -760,7 +760,7 @@ void State::commitToMine(BlockChain const& _bc) m_currentBlock.sha3Uncles = sha3(m_currentUncles); // Apply rewards last of all. - applyRewards(uncleAddresses); + applyRewards(uncleBlockHeaders); // Commit any and all changes to the trie that are in the cache, then update the state root accordingly. commit(); @@ -1148,12 +1148,12 @@ State State::fromPending(unsigned _i) const return ret; } -void State::applyRewards(Addresses const& _uncleAddresses) +void State::applyRewards(vector const& _uncleBlockHeaders) { u256 r = m_blockReward; - for (auto const& i: _uncleAddresses) + for (auto const& i: _uncleBlockHeaders) { - addBalance(i, m_blockReward * 15 / 16); + addBalance(i.coinbaseAddress, m_blockReward * (8 + i.number - m_currentBlock.number) / 8); r += m_blockReward / 32; } addBalance(m_currentBlock.coinbaseAddress, r); diff --git a/libethereum/State.h b/libethereum/State.h index 78ec85f38..1f8516fce 100644 --- a/libethereum/State.h +++ b/libethereum/State.h @@ -314,7 +314,7 @@ private: u256 enact(bytesConstRef _block, BlockChain const& _bc, bool _checkNonce = true); /// Finalise the block, applying the earned rewards. - void applyRewards(Addresses const& _uncleAddresses); + void applyRewards(std::vector const& _uncleBlockHeaders); /// @returns gas used by transactions thus far executed. u256 gasUsed() const { return m_receipts.size() ? m_receipts.back().gasUsed() : 0; } From b3db31fc3669d28b03633a2b6405787710f78868 Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Mon, 9 Mar 2015 13:53:02 +0100 Subject: [PATCH 051/100] pullSubtree.sh --- pullSubtree.sh | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100755 pullSubtree.sh diff --git a/pullSubtree.sh b/pullSubtree.sh new file mode 100755 index 000000000..5f20f5123 --- /dev/null +++ b/pullSubtree.sh @@ -0,0 +1,32 @@ +# usage +# ./pullsubtree [repository branch] [repository2 branch2] +# +# example +# ./pullSubtree evmjit master +# ./pullSubtree ethereumjs develop +# ./pullSubtree evmjit master ethereumjs master + +evmjit_repo="https://github.com/ethereum/evmjit" +evmjit_location="evmjit" + +ethereumjs_repo="https://github.com/ethereum/ethereum.js" +ethereumjs_location="libjsqrc/ethereumjs" + +natspecjs_repo="https://github.com/ethereum/natspec.js" +natspecjs_location="libnatspec/natspecjs" + +while [ "$1" != "" ]; do + case $1 in + evmjit | ethereumjs | natspecjs ) + REPO="${1}_repo" + REPO=${!REPO} + LOCATION="${1}_location" + LOCATION=${!LOCATION} + shift + BRANCH=$1 + git subtree pull --prefix=${LOCATION} ${REPO} ${BRANCH} --squash + ;; + esac + shift +done + From dc97c3d3ad736d79aeeb82baae6aa45015620846 Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Mon, 9 Mar 2015 14:22:29 +0100 Subject: [PATCH 052/100] Squashed 'libnatspec/natspecjs/' content from commit 4f31ed9 git-subtree-dir: libnatspec/natspecjs git-subtree-split: 4f31ed9698be96c347ea769b8b7e77a9507fb615 --- .gitignore | 3 + .travis.yml | 8 + README.md | 47 + dist/natspec.js | 3570 ++++++++++++++++++++++++++++++++++++++++++ dist/natspec.js.map | 1 + dist/natspec.min.js | 2 + example/example.html | 13 + natspec.js | 186 +++ package.json | 24 + test/test.js | 149 ++ 10 files changed, 4003 insertions(+) create mode 100644 .gitignore create mode 100644 .travis.yml create mode 100644 README.md create mode 100644 dist/natspec.js create mode 100644 dist/natspec.js.map create mode 100644 dist/natspec.min.js create mode 100644 example/example.html create mode 100644 natspec.js create mode 100644 package.json create mode 100644 test/test.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..95b26b70d --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +# VIM stuff +*.swp +node_modules/ diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..dca4078ce --- /dev/null +++ b/.travis.yml @@ -0,0 +1,8 @@ +language: node_js +node_js: + - "0.11" + - "0.10" +before_script: + - npm install +after_script: + - npm run-script test-coveralls diff --git a/README.md b/README.md new file mode 100644 index 000000000..63f08e47a --- /dev/null +++ b/README.md @@ -0,0 +1,47 @@ +# natspec.js +Javascript Library used to evaluate natspec expressions + +[![Build Status][travis-image]][travis-url] [![Coverage Status][coveralls-image]][coveralls-url] + +[travis-image]: https://travis-ci.org/ethereum/natspec.js.svg +[travis-url]: https://travis-ci.org/ethereum/natspec.js +[coveralls-image]: https://coveralls.io/repos/ethereum/natspec.js/badge.svg?branch=master +[coveralls-url]: https://coveralls.io/r/ethereum/natspec.js?branch=master + +## Usage + +It exposes global object `natspec` with method `evaluateExpression`. + +```javascript +var natspec = require('natspec'); + +var natspecExpression = "Will multiply `a` by 7 and return `a * 7`."; +var call = { + method: 'multiply', + abi: abi, + transaction: transaction +}; + +var evaluatedExpression = natspec.evaluateExpression(natspecExpression, call); +console.log(evaluatedExpression); // "Will multiply 4 by 7 and return 28." +``` + +More examples are available [here](https://github.com/ethereum/natspec.js/blob/master/test/test.js). + +## Building + +```bash +npm run-script build +``` + +## Testing (mocha) + +```bash +npm test +``` + +## Wiki + +* [Ethereum Natural Specification Format](https://github.com/ethereum/wiki/wiki/Ethereum-Natural-Specification-Format) +* [Natspec Example](https://github.com/ethereum/wiki/wiki/Natspec-Example) + diff --git a/dist/natspec.js b/dist/natspec.js new file mode 100644 index 000000000..a2256414b --- /dev/null +++ b/dist/natspec.js @@ -0,0 +1,3570 @@ +require=(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o. +*/ +/** @file abi.js + * @authors: + * Marek Kotewicz + * Gav Wood + * @date 2014 + */ + +var utils = require('./utils'); +var types = require('./types'); +var c = require('./const'); +var f = require('./formatters'); + +var displayTypeError = function (type) { + console.error('parser does not support type: ' + type); +}; + +/// This method should be called if we want to check if givent type is an array type +/// @returns true if it is, otherwise false +var arrayType = function (type) { + return type.slice(-2) === '[]'; +}; + +var dynamicTypeBytes = function (type, value) { + // TODO: decide what to do with array of strings + if (arrayType(type) || type === 'string') // only string itself that is dynamic; stringX is static length. + return f.formatInputInt(value.length); + return ""; +}; + +var inputTypes = types.inputTypes(); + +/// Formats input params to bytes +/// @param abi contract method inputs +/// @param array of params that will be formatted to bytes +/// @returns bytes representation of input params +var formatInput = function (inputs, params) { + var bytes = ""; + var toAppendConstant = ""; + var toAppendArrayContent = ""; + + /// first we iterate in search for dynamic + inputs.forEach(function (input, index) { + bytes += dynamicTypeBytes(input.type, params[index]); + }); + + inputs.forEach(function (input, i) { + /*jshint maxcomplexity:5 */ + var typeMatch = false; + for (var j = 0; j < inputTypes.length && !typeMatch; j++) { + typeMatch = inputTypes[j].type(inputs[i].type, params[i]); + } + if (!typeMatch) { + displayTypeError(inputs[i].type); + } + + var formatter = inputTypes[j - 1].format; + + if (arrayType(inputs[i].type)) + toAppendArrayContent += params[i].reduce(function (acc, curr) { + return acc + formatter(curr); + }, ""); + else if (inputs[i].type === 'string') + toAppendArrayContent += formatter(params[i]); + else + toAppendConstant += formatter(params[i]); + }); + + bytes += toAppendConstant + toAppendArrayContent; + + return bytes; +}; + +var dynamicBytesLength = function (type) { + if (arrayType(type) || type === 'string') // only string itself that is dynamic; stringX is static length. + return c.ETH_PADDING * 2; + return 0; +}; + +var outputTypes = types.outputTypes(); + +/// Formats output bytes back to param list +/// @param contract abi method outputs +/// @param bytes representtion of output +/// @returns array of output params +var formatOutput = function (outs, output) { + + output = output.slice(2); + var result = []; + var padding = c.ETH_PADDING * 2; + + var dynamicPartLength = outs.reduce(function (acc, curr) { + return acc + dynamicBytesLength(curr.type); + }, 0); + + var dynamicPart = output.slice(0, dynamicPartLength); + output = output.slice(dynamicPartLength); + + outs.forEach(function (out, i) { + /*jshint maxcomplexity:6 */ + var typeMatch = false; + for (var j = 0; j < outputTypes.length && !typeMatch; j++) { + typeMatch = outputTypes[j].type(outs[i].type); + } + + if (!typeMatch) { + displayTypeError(outs[i].type); + } + + var formatter = outputTypes[j - 1].format; + if (arrayType(outs[i].type)) { + var size = f.formatOutputUInt(dynamicPart.slice(0, padding)); + dynamicPart = dynamicPart.slice(padding); + var array = []; + for (var k = 0; k < size; k++) { + array.push(formatter(output.slice(0, padding))); + output = output.slice(padding); + } + result.push(array); + } + else if (types.prefixedType('string')(outs[i].type)) { + dynamicPart = dynamicPart.slice(padding); + result.push(formatter(output.slice(0, padding))); + output = output.slice(padding); + } else { + result.push(formatter(output.slice(0, padding))); + output = output.slice(padding); + } + }); + + return result; +}; + +/// @param json abi for contract +/// @returns input parser object for given json abi +/// TODO: refactor creating the parser, do not double logic from contract +var inputParser = function (json) { + var parser = {}; + json.forEach(function (method) { + var displayName = utils.extractDisplayName(method.name); + var typeName = utils.extractTypeName(method.name); + + var impl = function () { + var params = Array.prototype.slice.call(arguments); + return formatInput(method.inputs, params); + }; + + if (parser[displayName] === undefined) { + parser[displayName] = impl; + } + + parser[displayName][typeName] = impl; + }); + + return parser; +}; + +/// @param json abi for contract +/// @returns output parser for given json abi +var outputParser = function (json) { + var parser = {}; + json.forEach(function (method) { + + var displayName = utils.extractDisplayName(method.name); + var typeName = utils.extractTypeName(method.name); + + var impl = function (output) { + return formatOutput(method.outputs, output); + }; + + if (parser[displayName] === undefined) { + parser[displayName] = impl; + } + + parser[displayName][typeName] = impl; + }); + + return parser; +}; + +module.exports = { + inputParser: inputParser, + outputParser: outputParser, + formatInput: formatInput, + formatOutput: formatOutput +}; + +},{"./const":4,"./formatters":5,"./types":6,"./utils":7}],4:[function(require,module,exports){ +(function (process){ +/* + This file is part of ethereum.js. + + ethereum.js is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + ethereum.js is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with ethereum.js. If not, see . +*/ +/** @file const.js + * @authors: + * Marek Kotewicz + * @date 2015 + */ + +/// required to define ETH_BIGNUMBER_ROUNDING_MODE +if (process.env.NODE_ENV !== 'build') { + var BigNumber = require('bignumber.js'); // jshint ignore:line +} + +var ETH_UNITS = [ + 'wei', + 'Kwei', + 'Mwei', + 'Gwei', + 'szabo', + 'finney', + 'ether', + 'grand', + 'Mether', + 'Gether', + 'Tether', + 'Pether', + 'Eether', + 'Zether', + 'Yether', + 'Nether', + 'Dether', + 'Vether', + 'Uether' +]; + +module.exports = { + ETH_PADDING: 32, + ETH_SIGNATURE_LENGTH: 4, + ETH_UNITS: ETH_UNITS, + ETH_BIGNUMBER_ROUNDING_MODE: { ROUNDING_MODE: BigNumber.ROUND_DOWN }, + ETH_POLLING_TIMEOUT: 1000 +}; + + +}).call(this,require('_process')) +},{"_process":2,"bignumber.js":8}],5:[function(require,module,exports){ +(function (process){ +/* + This file is part of ethereum.js. + + ethereum.js is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + ethereum.js is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with ethereum.js. If not, see . +*/ +/** @file formatters.js + * @authors: + * Marek Kotewicz + * @date 2015 + */ + +if (process.env.NODE_ENV !== 'build') { + var BigNumber = require('bignumber.js'); // jshint ignore:line +} + +var utils = require('./utils'); +var c = require('./const'); + +/// @param string string to be padded +/// @param number of characters that result string should have +/// @param sign, by default 0 +/// @returns right aligned string +var padLeft = function (string, chars, sign) { + return new Array(chars - string.length + 1).join(sign ? sign : "0") + string; +}; + +/// Formats input value to byte representation of int +/// If value is negative, return it's two's complement +/// If the value is floating point, round it down +/// @returns right-aligned byte representation of int +var formatInputInt = function (value) { + /*jshint maxcomplexity:7 */ + var padding = c.ETH_PADDING * 2; + if (value instanceof BigNumber || typeof value === 'number') { + if (typeof value === 'number') + value = new BigNumber(value); + BigNumber.config(c.ETH_BIGNUMBER_ROUNDING_MODE); + value = value.round(); + + if (value.lessThan(0)) + value = new BigNumber("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16).plus(value).plus(1); + value = value.toString(16); + } + else if (value.indexOf('0x') === 0) + value = value.substr(2); + else if (typeof value === 'string') + value = formatInputInt(new BigNumber(value)); + else + value = (+value).toString(16); + return padLeft(value, padding); +}; + +/// Formats input value to byte representation of string +/// @returns left-algined byte representation of string +var formatInputString = function (value) { + return utils.fromAscii(value, c.ETH_PADDING).substr(2); +}; + +/// Formats input value to byte representation of bool +/// @returns right-aligned byte representation bool +var formatInputBool = function (value) { + return '000000000000000000000000000000000000000000000000000000000000000' + (value ? '1' : '0'); +}; + +/// Formats input value to byte representation of real +/// Values are multiplied by 2^m and encoded as integers +/// @returns byte representation of real +var formatInputReal = function (value) { + return formatInputInt(new BigNumber(value).times(new BigNumber(2).pow(128))); +}; + + +/// Check if input value is negative +/// @param value is hex format +/// @returns true if it is negative, otherwise false +var signedIsNegative = function (value) { + return (new BigNumber(value.substr(0, 1), 16).toString(2).substr(0, 1)) === '1'; +}; + +/// Formats input right-aligned input bytes to int +/// @returns right-aligned input bytes formatted to int +var formatOutputInt = function (value) { + value = value || "0"; + // check if it's negative number + // it it is, return two's complement + if (signedIsNegative(value)) { + return new BigNumber(value, 16).minus(new BigNumber('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 16)).minus(1); + } + return new BigNumber(value, 16); +}; + +/// Formats big right-aligned input bytes to uint +/// @returns right-aligned input bytes formatted to uint +var formatOutputUInt = function (value) { + value = value || "0"; + return new BigNumber(value, 16); +}; + +/// @returns input bytes formatted to real +var formatOutputReal = function (value) { + return formatOutputInt(value).dividedBy(new BigNumber(2).pow(128)); +}; + +/// @returns input bytes formatted to ureal +var formatOutputUReal = function (value) { + return formatOutputUInt(value).dividedBy(new BigNumber(2).pow(128)); +}; + +/// @returns right-aligned input bytes formatted to hex +var formatOutputHash = function (value) { + return "0x" + value; +}; + +/// @returns right-aligned input bytes formatted to bool +var formatOutputBool = function (value) { + return value === '0000000000000000000000000000000000000000000000000000000000000001' ? true : false; +}; + +/// @returns left-aligned input bytes formatted to ascii string +var formatOutputString = function (value) { + return utils.toAscii(value); +}; + +/// @returns right-aligned input bytes formatted to address +var formatOutputAddress = function (value) { + return "0x" + value.slice(value.length - 40, value.length); +}; + + +module.exports = { + formatInputInt: formatInputInt, + formatInputString: formatInputString, + formatInputBool: formatInputBool, + formatInputReal: formatInputReal, + formatOutputInt: formatOutputInt, + formatOutputUInt: formatOutputUInt, + formatOutputReal: formatOutputReal, + formatOutputUReal: formatOutputUReal, + formatOutputHash: formatOutputHash, + formatOutputBool: formatOutputBool, + formatOutputString: formatOutputString, + formatOutputAddress: formatOutputAddress +}; + + +}).call(this,require('_process')) +},{"./const":4,"./utils":7,"_process":2,"bignumber.js":8}],6:[function(require,module,exports){ +/* + This file is part of ethereum.js. + + ethereum.js is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + ethereum.js is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with ethereum.js. If not, see . +*/ +/** @file types.js + * @authors: + * Marek Kotewicz + * @date 2015 + */ + +var f = require('./formatters'); + +/// @param expected type prefix (string) +/// @returns function which checks if type has matching prefix. if yes, returns true, otherwise false +var prefixedType = function (prefix) { + return function (type) { + return type.indexOf(prefix) === 0; + }; +}; + +/// @param expected type name (string) +/// @returns function which checks if type is matching expected one. if yes, returns true, otherwise false +var namedType = function (name) { + return function (type) { + return name === type; + }; +}; + +/// Setups input formatters for solidity types +/// @returns an array of input formatters +var inputTypes = function () { + + return [ + { type: prefixedType('uint'), format: f.formatInputInt }, + { type: prefixedType('int'), format: f.formatInputInt }, + { type: prefixedType('hash'), format: f.formatInputInt }, + { type: prefixedType('string'), format: f.formatInputString }, + { type: prefixedType('real'), format: f.formatInputReal }, + { type: prefixedType('ureal'), format: f.formatInputReal }, + { type: namedType('address'), format: f.formatInputInt }, + { type: namedType('bool'), format: f.formatInputBool } + ]; +}; + +/// Setups output formaters for solidity types +/// @returns an array of output formatters +var outputTypes = function () { + + return [ + { type: prefixedType('uint'), format: f.formatOutputUInt }, + { type: prefixedType('int'), format: f.formatOutputInt }, + { type: prefixedType('hash'), format: f.formatOutputHash }, + { type: prefixedType('string'), format: f.formatOutputString }, + { type: prefixedType('real'), format: f.formatOutputReal }, + { type: prefixedType('ureal'), format: f.formatOutputUReal }, + { type: namedType('address'), format: f.formatOutputAddress }, + { type: namedType('bool'), format: f.formatOutputBool } + ]; +}; + +module.exports = { + prefixedType: prefixedType, + namedType: namedType, + inputTypes: inputTypes, + outputTypes: outputTypes +}; + + +},{"./formatters":5}],7:[function(require,module,exports){ +/* + This file is part of ethereum.js. + + ethereum.js is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + ethereum.js is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with ethereum.js. If not, see . +*/ +/** @file utils.js + * @authors: + * Marek Kotewicz + * @date 2015 + */ + +var c = require('./const'); + +/// Finds first index of array element matching pattern +/// @param array +/// @param callback pattern +/// @returns index of element +var findIndex = function (array, callback) { + var end = false; + var i = 0; + for (; i < array.length && !end; i++) { + end = callback(array[i]); + } + return end ? i - 1 : -1; +}; + +/// @returns ascii string representation of hex value prefixed with 0x +var toAscii = function(hex) { +// Find termination + var str = ""; + var i = 0, l = hex.length; + if (hex.substring(0, 2) === '0x') { + i = 2; + } + for (; i < l; i+=2) { + var code = parseInt(hex.substr(i, 2), 16); + if (code === 0) { + break; + } + + str += String.fromCharCode(code); + } + + return str; +}; + +var toHex = function(str) { + var hex = ""; + for(var i = 0; i < str.length; i++) { + var n = str.charCodeAt(i).toString(16); + hex += n.length < 2 ? '0' + n : n; + } + + return hex; +}; + +/// @returns hex representation (prefixed by 0x) of ascii string +var fromAscii = function(str, pad) { + pad = pad === undefined ? 0 : pad; + var hex = toHex(str); + while (hex.length < pad*2) + hex += "00"; + return "0x" + hex; +}; + +/// @returns display name for function/event eg. multiply(uint256) -> multiply +var extractDisplayName = function (name) { + var length = name.indexOf('('); + return length !== -1 ? name.substr(0, length) : name; +}; + +/// @returns overloaded part of function/event name +var extractTypeName = function (name) { + /// TODO: make it invulnerable + var length = name.indexOf('('); + return length !== -1 ? name.substr(length + 1, name.length - 1 - (length + 1)).replace(' ', '') : ""; +}; + +/// Filters all function from input abi +/// @returns abi array with filtered objects of type 'function' +var filterFunctions = function (json) { + return json.filter(function (current) { + return current.type === 'function'; + }); +}; + +/// Filters all events form input abi +/// @returns abi array with filtered objects of type 'event' +var filterEvents = function (json) { + return json.filter(function (current) { + return current.type === 'event'; + }); +}; + +/// used to transform value/string to eth string +/// TODO: use BigNumber.js to parse int +/// TODO: add tests for it! +var toEth = function (str) { + /*jshint maxcomplexity:7 */ + var val = typeof str === "string" ? str.indexOf('0x') === 0 ? parseInt(str.substr(2), 16) : parseInt(str) : str; + var unit = 0; + var units = c.ETH_UNITS; + while (val > 3000 && unit < units.length - 1) + { + val /= 1000; + unit++; + } + var s = val.toString().length < val.toFixed(2).length ? val.toString() : val.toFixed(2); + var replaceFunction = function($0, $1, $2) { + return $1 + ',' + $2; + }; + + while (true) { + var o = s; + s = s.replace(/(\d)(\d\d\d[\.\,])/, replaceFunction); + if (o === s) + break; + } + return s + ' ' + units[unit]; +}; + +module.exports = { + findIndex: findIndex, + toAscii: toAscii, + fromAscii: fromAscii, + extractDisplayName: extractDisplayName, + extractTypeName: extractTypeName, + filterFunctions: filterFunctions, + filterEvents: filterEvents, + toEth: toEth +}; + + +},{"./const":4}],8:[function(require,module,exports){ +/*! bignumber.js v2.0.3 https://github.com/MikeMcl/bignumber.js/LICENCE */ + +;(function (global) { + 'use strict'; + + /* + bignumber.js v2.0.3 + A JavaScript library for arbitrary-precision arithmetic. + https://github.com/MikeMcl/bignumber.js + Copyright (c) 2015 Michael Mclaughlin + MIT Expat Licence + */ + + + var BigNumber, crypto, parseNumeric, + isNumeric = /^-?(\d+(\.\d*)?|\.\d+)(e[+-]?\d+)?$/i, + mathceil = Math.ceil, + mathfloor = Math.floor, + notBool = ' not a boolean or binary digit', + roundingMode = 'rounding mode', + tooManyDigits = 'number type has more than 15 significant digits', + ALPHABET = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_', + BASE = 1e14, + LOG_BASE = 14, + MAX_SAFE_INTEGER = 0x1fffffffffffff, // 2^53 - 1 + // MAX_INT32 = 0x7fffffff, // 2^31 - 1 + POWS_TEN = [1, 10, 100, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e12, 1e13], + SQRT_BASE = 1e7, + + /* + * The limit on the value of DECIMAL_PLACES, TO_EXP_NEG, TO_EXP_POS, MIN_EXP, MAX_EXP, and + * the arguments to toExponential, toFixed, toFormat, and toPrecision, beyond which an + * exception is thrown (if ERRORS is true). + */ + MAX = 1E9; // 0 to MAX_INT32 + + + /* + * Create and return a BigNumber constructor. + */ + function another(configObj) { + var div, + + // id tracks the caller function, so its name can be included in error messages. + id = 0, + P = BigNumber.prototype, + ONE = new BigNumber(1), + + + /********************************* EDITABLE DEFAULTS **********************************/ + + + /* + * The default values below must be integers within the inclusive ranges stated. + * The values can also be changed at run-time using BigNumber.config. + */ + + // The maximum number of decimal places for operations involving division. + DECIMAL_PLACES = 20, // 0 to MAX + + /* + * The rounding mode used when rounding to the above decimal places, and when using + * toExponential, toFixed, toFormat and toPrecision, and round (default value). + * UP 0 Away from zero. + * DOWN 1 Towards zero. + * CEIL 2 Towards +Infinity. + * FLOOR 3 Towards -Infinity. + * HALF_UP 4 Towards nearest neighbour. If equidistant, up. + * HALF_DOWN 5 Towards nearest neighbour. If equidistant, down. + * HALF_EVEN 6 Towards nearest neighbour. If equidistant, towards even neighbour. + * HALF_CEIL 7 Towards nearest neighbour. If equidistant, towards +Infinity. + * HALF_FLOOR 8 Towards nearest neighbour. If equidistant, towards -Infinity. + */ + ROUNDING_MODE = 4, // 0 to 8 + + // EXPONENTIAL_AT : [TO_EXP_NEG , TO_EXP_POS] + + // The exponent value at and beneath which toString returns exponential notation. + // Number type: -7 + TO_EXP_NEG = -7, // 0 to -MAX + + // The exponent value at and above which toString returns exponential notation. + // Number type: 21 + TO_EXP_POS = 21, // 0 to MAX + + // RANGE : [MIN_EXP, MAX_EXP] + + // The minimum exponent value, beneath which underflow to zero occurs. + // Number type: -324 (5e-324) + MIN_EXP = -1e7, // -1 to -MAX + + // The maximum exponent value, above which overflow to Infinity occurs. + // Number type: 308 (1.7976931348623157e+308) + // For MAX_EXP > 1e7, e.g. new BigNumber('1e100000000').plus(1) may be slow. + MAX_EXP = 1e7, // 1 to MAX + + // Whether BigNumber Errors are ever thrown. + ERRORS = true, // true or false + + // Change to intValidatorNoErrors if ERRORS is false. + isValidInt = intValidatorWithErrors, // intValidatorWithErrors/intValidatorNoErrors + + // Whether to use cryptographically-secure random number generation, if available. + CRYPTO = false, // true or false + + /* + * The modulo mode used when calculating the modulus: a mod n. + * The quotient (q = a / n) is calculated according to the corresponding rounding mode. + * The remainder (r) is calculated as: r = a - n * q. + * + * UP 0 The remainder is positive if the dividend is negative, else is negative. + * DOWN 1 The remainder has the same sign as the dividend. + * This modulo mode is commonly known as 'truncated division' and is + * equivalent to (a % n) in JavaScript. + * FLOOR 3 The remainder has the same sign as the divisor (Python %). + * HALF_EVEN 6 This modulo mode implements the IEEE 754 remainder function. + * EUCLID 9 Euclidian division. q = sign(n) * floor(a / abs(n)). + * The remainder is always positive. + * + * The truncated division, floored division, Euclidian division and IEEE 754 remainder + * modes are commonly used for the modulus operation. + * Although the other rounding modes can also be used, they may not give useful results. + */ + MODULO_MODE = 1, // 0 to 9 + + // The maximum number of significant digits of the result of the toPower operation. + // If POW_PRECISION is 0, there will be unlimited significant digits. + POW_PRECISION = 100, // 0 to MAX + + // The format specification used by the BigNumber.prototype.toFormat method. + FORMAT = { + decimalSeparator: '.', + groupSeparator: ',', + groupSize: 3, + secondaryGroupSize: 0, + fractionGroupSeparator: '\xA0', // non-breaking space + fractionGroupSize: 0 + }; + + + /******************************************************************************************/ + + + // CONSTRUCTOR + + + /* + * The BigNumber constructor and exported function. + * Create and return a new instance of a BigNumber object. + * + * n {number|string|BigNumber} A numeric value. + * [b] {number} The base of n. Integer, 2 to 64 inclusive. + */ + function BigNumber( n, b ) { + var c, e, i, num, len, str, + x = this; + + // Enable constructor usage without new. + if ( !( x instanceof BigNumber ) ) { + + // 'BigNumber() constructor call without new: {n}' + if (ERRORS) raise( 26, 'constructor call without new', n ); + return new BigNumber( n, b ); + } + + // 'new BigNumber() base not an integer: {b}' + // 'new BigNumber() base out of range: {b}' + if ( b == null || !isValidInt( b, 2, 64, id, 'base' ) ) { + + // Duplicate. + if ( n instanceof BigNumber ) { + x.s = n.s; + x.e = n.e; + x.c = ( n = n.c ) ? n.slice() : n; + id = 0; + return; + } + + if ( ( num = typeof n == 'number' ) && n * 0 == 0 ) { + x.s = 1 / n < 0 ? ( n = -n, -1 ) : 1; + + // Fast path for integers. + if ( n === ~~n ) { + for ( e = 0, i = n; i >= 10; i /= 10, e++ ); + x.e = e; + x.c = [n]; + id = 0; + return; + } + + str = n + ''; + } else { + if ( !isNumeric.test( str = n + '' ) ) return parseNumeric( x, str, num ); + x.s = str.charCodeAt(0) === 45 ? ( str = str.slice(1), -1 ) : 1; + } + } else { + b = b | 0; + str = n + ''; + + // Ensure return value is rounded to DECIMAL_PLACES as with other bases. + // Allow exponential notation to be used with base 10 argument. + if ( b == 10 ) { + x = new BigNumber( n instanceof BigNumber ? n : str ); + return round( x, DECIMAL_PLACES + x.e + 1, ROUNDING_MODE ); + } + + // Avoid potential interpretation of Infinity and NaN as base 44+ values. + // Any number in exponential form will fail due to the [Ee][+-]. + if ( ( num = typeof n == 'number' ) && n * 0 != 0 || + !( new RegExp( '^-?' + ( c = '[' + ALPHABET.slice( 0, b ) + ']+' ) + + '(?:\\.' + c + ')?$',b < 37 ? 'i' : '' ) ).test(str) ) { + return parseNumeric( x, str, num, b ); + } + + if (num) { + x.s = 1 / n < 0 ? ( str = str.slice(1), -1 ) : 1; + + if ( ERRORS && str.replace( /^0\.0*|\./, '' ).length > 15 ) { + + // 'new BigNumber() number type has more than 15 significant digits: {n}' + raise( id, tooManyDigits, n ); + } + + // Prevent later check for length on converted number. + num = false; + } else { + x.s = str.charCodeAt(0) === 45 ? ( str = str.slice(1), -1 ) : 1; + } + + str = convertBase( str, 10, b, x.s ); + } + + // Decimal point? + if ( ( e = str.indexOf('.') ) > -1 ) str = str.replace( '.', '' ); + + // Exponential form? + if ( ( i = str.search( /e/i ) ) > 0 ) { + + // Determine exponent. + if ( e < 0 ) e = i; + e += +str.slice( i + 1 ); + str = str.substring( 0, i ); + } else if ( e < 0 ) { + + // Integer. + e = str.length; + } + + // Determine leading zeros. + for ( i = 0; str.charCodeAt(i) === 48; i++ ); + + // Determine trailing zeros. + for ( len = str.length; str.charCodeAt(--len) === 48; ); + str = str.slice( i, len + 1 ); + + if (str) { + len = str.length; + + // Disallow numbers with over 15 significant digits if number type. + // 'new BigNumber() number type has more than 15 significant digits: {n}' + if ( num && ERRORS && len > 15 ) raise( id, tooManyDigits, x.s * n ); + + e = e - i - 1; + + // Overflow? + if ( e > MAX_EXP ) { + + // Infinity. + x.c = x.e = null; + + // Underflow? + } else if ( e < MIN_EXP ) { + + // Zero. + x.c = [ x.e = 0 ]; + } else { + x.e = e; + x.c = []; + + // Transform base + + // e is the base 10 exponent. + // i is where to slice str to get the first element of the coefficient array. + i = ( e + 1 ) % LOG_BASE; + if ( e < 0 ) i += LOG_BASE; + + if ( i < len ) { + if (i) x.c.push( +str.slice( 0, i ) ); + + for ( len -= LOG_BASE; i < len; ) { + x.c.push( +str.slice( i, i += LOG_BASE ) ); + } + + str = str.slice(i); + i = LOG_BASE - str.length; + } else { + i -= len; + } + + for ( ; i--; str += '0' ); + x.c.push( +str ); + } + } else { + + // Zero. + x.c = [ x.e = 0 ]; + } + + id = 0; + } + + + // CONSTRUCTOR PROPERTIES + + + BigNumber.another = another; + + BigNumber.ROUND_UP = 0; + BigNumber.ROUND_DOWN = 1; + BigNumber.ROUND_CEIL = 2; + BigNumber.ROUND_FLOOR = 3; + BigNumber.ROUND_HALF_UP = 4; + BigNumber.ROUND_HALF_DOWN = 5; + BigNumber.ROUND_HALF_EVEN = 6; + BigNumber.ROUND_HALF_CEIL = 7; + BigNumber.ROUND_HALF_FLOOR = 8; + BigNumber.EUCLID = 9; + + + /* + * Configure infrequently-changing library-wide settings. + * + * Accept an object or an argument list, with one or many of the following properties or + * parameters respectively: + * + * DECIMAL_PLACES {number} Integer, 0 to MAX inclusive + * ROUNDING_MODE {number} Integer, 0 to 8 inclusive + * EXPONENTIAL_AT {number|number[]} Integer, -MAX to MAX inclusive or + * [integer -MAX to 0 incl., 0 to MAX incl.] + * RANGE {number|number[]} Non-zero integer, -MAX to MAX inclusive or + * [integer -MAX to -1 incl., integer 1 to MAX incl.] + * ERRORS {boolean|number} true, false, 1 or 0 + * CRYPTO {boolean|number} true, false, 1 or 0 + * MODULO_MODE {number} 0 to 9 inclusive + * POW_PRECISION {number} 0 to MAX inclusive + * FORMAT {object} See BigNumber.prototype.toFormat + * decimalSeparator {string} + * groupSeparator {string} + * groupSize {number} + * secondaryGroupSize {number} + * fractionGroupSeparator {string} + * fractionGroupSize {number} + * + * (The values assigned to the above FORMAT object properties are not checked for validity.) + * + * E.g. + * BigNumber.config(20, 4) is equivalent to + * BigNumber.config({ DECIMAL_PLACES : 20, ROUNDING_MODE : 4 }) + * + * Ignore properties/parameters set to null or undefined. + * Return an object with the properties current values. + */ + BigNumber.config = function () { + var v, p, + i = 0, + r = {}, + a = arguments, + o = a[0], + has = o && typeof o == 'object' + ? function () { if ( o.hasOwnProperty(p) ) return ( v = o[p] ) != null; } + : function () { if ( a.length > i ) return ( v = a[i++] ) != null; }; + + // DECIMAL_PLACES {number} Integer, 0 to MAX inclusive. + // 'config() DECIMAL_PLACES not an integer: {v}' + // 'config() DECIMAL_PLACES out of range: {v}' + if ( has( p = 'DECIMAL_PLACES' ) && isValidInt( v, 0, MAX, 2, p ) ) { + DECIMAL_PLACES = v | 0; + } + r[p] = DECIMAL_PLACES; + + // ROUNDING_MODE {number} Integer, 0 to 8 inclusive. + // 'config() ROUNDING_MODE not an integer: {v}' + // 'config() ROUNDING_MODE out of range: {v}' + if ( has( p = 'ROUNDING_MODE' ) && isValidInt( v, 0, 8, 2, p ) ) { + ROUNDING_MODE = v | 0; + } + r[p] = ROUNDING_MODE; + + // EXPONENTIAL_AT {number|number[]} + // Integer, -MAX to MAX inclusive or [integer -MAX to 0 inclusive, 0 to MAX inclusive]. + // 'config() EXPONENTIAL_AT not an integer: {v}' + // 'config() EXPONENTIAL_AT out of range: {v}' + if ( has( p = 'EXPONENTIAL_AT' ) ) { + + if ( isArray(v) ) { + if ( isValidInt( v[0], -MAX, 0, 2, p ) && isValidInt( v[1], 0, MAX, 2, p ) ) { + TO_EXP_NEG = v[0] | 0; + TO_EXP_POS = v[1] | 0; + } + } else if ( isValidInt( v, -MAX, MAX, 2, p ) ) { + TO_EXP_NEG = -( TO_EXP_POS = ( v < 0 ? -v : v ) | 0 ); + } + } + r[p] = [ TO_EXP_NEG, TO_EXP_POS ]; + + // RANGE {number|number[]} Non-zero integer, -MAX to MAX inclusive or + // [integer -MAX to -1 inclusive, integer 1 to MAX inclusive]. + // 'config() RANGE not an integer: {v}' + // 'config() RANGE cannot be zero: {v}' + // 'config() RANGE out of range: {v}' + if ( has( p = 'RANGE' ) ) { + + if ( isArray(v) ) { + if ( isValidInt( v[0], -MAX, -1, 2, p ) && isValidInt( v[1], 1, MAX, 2, p ) ) { + MIN_EXP = v[0] | 0; + MAX_EXP = v[1] | 0; + } + } else if ( isValidInt( v, -MAX, MAX, 2, p ) ) { + if ( v | 0 ) MIN_EXP = -( MAX_EXP = ( v < 0 ? -v : v ) | 0 ); + else if (ERRORS) raise( 2, p + ' cannot be zero', v ); + } + } + r[p] = [ MIN_EXP, MAX_EXP ]; + + // ERRORS {boolean|number} true, false, 1 or 0. + // 'config() ERRORS not a boolean or binary digit: {v}' + if ( has( p = 'ERRORS' ) ) { + + if ( v === !!v || v === 1 || v === 0 ) { + id = 0; + isValidInt = ( ERRORS = !!v ) ? intValidatorWithErrors : intValidatorNoErrors; + } else if (ERRORS) { + raise( 2, p + notBool, v ); + } + } + r[p] = ERRORS; + + // CRYPTO {boolean|number} true, false, 1 or 0. + // 'config() CRYPTO not a boolean or binary digit: {v}' + // 'config() crypto unavailable: {crypto}' + if ( has( p = 'CRYPTO' ) ) { + + if ( v === !!v || v === 1 || v === 0 ) { + CRYPTO = !!( v && crypto && typeof crypto == 'object' ); + if ( v && !CRYPTO && ERRORS ) raise( 2, 'crypto unavailable', crypto ); + } else if (ERRORS) { + raise( 2, p + notBool, v ); + } + } + r[p] = CRYPTO; + + // MODULO_MODE {number} Integer, 0 to 9 inclusive. + // 'config() MODULO_MODE not an integer: {v}' + // 'config() MODULO_MODE out of range: {v}' + if ( has( p = 'MODULO_MODE' ) && isValidInt( v, 0, 9, 2, p ) ) { + MODULO_MODE = v | 0; + } + r[p] = MODULO_MODE; + + // POW_PRECISION {number} Integer, 0 to MAX inclusive. + // 'config() POW_PRECISION not an integer: {v}' + // 'config() POW_PRECISION out of range: {v}' + if ( has( p = 'POW_PRECISION' ) && isValidInt( v, 0, MAX, 2, p ) ) { + POW_PRECISION = v | 0; + } + r[p] = POW_PRECISION; + + // FORMAT {object} + // 'config() FORMAT not an object: {v}' + if ( has( p = 'FORMAT' ) ) { + + if ( typeof v == 'object' ) { + FORMAT = v; + } else if (ERRORS) { + raise( 2, p + ' not an object', v ); + } + } + r[p] = FORMAT; + + return r; + }; + + + /* + * Return a new BigNumber whose value is the maximum of the arguments. + * + * arguments {number|string|BigNumber} + */ + BigNumber.max = function () { return maxOrMin( arguments, P.lt ); }; + + + /* + * Return a new BigNumber whose value is the minimum of the arguments. + * + * arguments {number|string|BigNumber} + */ + BigNumber.min = function () { return maxOrMin( arguments, P.gt ); }; + + + /* + * Return a new BigNumber with a random value equal to or greater than 0 and less than 1, + * and with dp, or DECIMAL_PLACES if dp is omitted, decimal places (or less if trailing + * zeros are produced). + * + * [dp] {number} Decimal places. Integer, 0 to MAX inclusive. + * + * 'random() decimal places not an integer: {dp}' + * 'random() decimal places out of range: {dp}' + * 'random() crypto unavailable: {crypto}' + */ + BigNumber.random = (function () { + var pow2_53 = 0x20000000000000; + + // Return a 53 bit integer n, where 0 <= n < 9007199254740992. + // Check if Math.random() produces more than 32 bits of randomness. + // If it does, assume at least 53 bits are produced, otherwise assume at least 30 bits. + // 0x40000000 is 2^30, 0x800000 is 2^23, 0x1fffff is 2^21 - 1. + var random53bitInt = (Math.random() * pow2_53) & 0x1fffff + ? function () { return mathfloor( Math.random() * pow2_53 ); } + : function () { return ((Math.random() * 0x40000000 | 0) * 0x800000) + + (Math.random() * 0x800000 | 0); }; + + return function (dp) { + var a, b, e, k, v, + i = 0, + c = [], + rand = new BigNumber(ONE); + + dp = dp == null || !isValidInt( dp, 0, MAX, 14 ) ? DECIMAL_PLACES : dp | 0; + k = mathceil( dp / LOG_BASE ); + + if (CRYPTO) { + + // Browsers supporting crypto.getRandomValues. + if ( crypto && crypto.getRandomValues ) { + + a = crypto.getRandomValues( new Uint32Array( k *= 2 ) ); + + for ( ; i < k; ) { + + // 53 bits: + // ((Math.pow(2, 32) - 1) * Math.pow(2, 21)).toString(2) + // 11111 11111111 11111111 11111111 11100000 00000000 00000000 + // ((Math.pow(2, 32) - 1) >>> 11).toString(2) + // 11111 11111111 11111111 + // 0x20000 is 2^21. + v = a[i] * 0x20000 + (a[i + 1] >>> 11); + + // Rejection sampling: + // 0 <= v < 9007199254740992 + // Probability that v >= 9e15, is + // 7199254740992 / 9007199254740992 ~= 0.0008, i.e. 1 in 1251 + if ( v >= 9e15 ) { + b = crypto.getRandomValues( new Uint32Array(2) ); + a[i] = b[0]; + a[i + 1] = b[1]; + } else { + + // 0 <= v <= 8999999999999999 + // 0 <= (v % 1e14) <= 99999999999999 + c.push( v % 1e14 ); + i += 2; + } + } + i = k / 2; + + // Node.js supporting crypto.randomBytes. + } else if ( crypto && crypto.randomBytes ) { + + // buffer + a = crypto.randomBytes( k *= 7 ); + + for ( ; i < k; ) { + + // 0x1000000000000 is 2^48, 0x10000000000 is 2^40 + // 0x100000000 is 2^32, 0x1000000 is 2^24 + // 11111 11111111 11111111 11111111 11111111 11111111 11111111 + // 0 <= v < 9007199254740992 + v = ( ( a[i] & 31 ) * 0x1000000000000 ) + ( a[i + 1] * 0x10000000000 ) + + ( a[i + 2] * 0x100000000 ) + ( a[i + 3] * 0x1000000 ) + + ( a[i + 4] << 16 ) + ( a[i + 5] << 8 ) + a[i + 6]; + + if ( v >= 9e15 ) { + crypto.randomBytes(7).copy( a, i ); + } else { + + // 0 <= (v % 1e14) <= 99999999999999 + c.push( v % 1e14 ); + i += 7; + } + } + i = k / 7; + } else if (ERRORS) { + raise( 14, 'crypto unavailable', crypto ); + } + } + + // Use Math.random: CRYPTO is false or crypto is unavailable and ERRORS is false. + if (!i) { + + for ( ; i < k; ) { + v = random53bitInt(); + if ( v < 9e15 ) c[i++] = v % 1e14; + } + } + + k = c[--i]; + dp %= LOG_BASE; + + // Convert trailing digits to zeros according to dp. + if ( k && dp ) { + v = POWS_TEN[LOG_BASE - dp]; + c[i] = mathfloor( k / v ) * v; + } + + // Remove trailing elements which are zero. + for ( ; c[i] === 0; c.pop(), i-- ); + + // Zero? + if ( i < 0 ) { + c = [ e = 0 ]; + } else { + + // Remove leading elements which are zero and adjust exponent accordingly. + for ( e = -1 ; c[0] === 0; c.shift(), e -= LOG_BASE); + + // Count the digits of the first element of c to determine leading zeros, and... + for ( i = 1, v = c[0]; v >= 10; v /= 10, i++); + + // adjust the exponent accordingly. + if ( i < LOG_BASE ) e -= LOG_BASE - i; + } + + rand.e = e; + rand.c = c; + return rand; + }; + })(); + + + // PRIVATE FUNCTIONS + + + // Convert a numeric string of baseIn to a numeric string of baseOut. + function convertBase( str, baseOut, baseIn, sign ) { + var d, e, k, r, x, xc, y, + i = str.indexOf( '.' ), + dp = DECIMAL_PLACES, + rm = ROUNDING_MODE; + + if ( baseIn < 37 ) str = str.toLowerCase(); + + // Non-integer. + if ( i >= 0 ) { + k = POW_PRECISION; + + // Unlimited precision. + POW_PRECISION = 0; + str = str.replace( '.', '' ); + y = new BigNumber(baseIn); + x = y.pow( str.length - i ); + POW_PRECISION = k; + + // Convert str as if an integer, then restore the fraction part by dividing the + // result by its base raised to a power. + y.c = toBaseOut( toFixedPoint( coeffToString( x.c ), x.e ), 10, baseOut ); + y.e = y.c.length; + } + + // Convert the number as integer. + xc = toBaseOut( str, baseIn, baseOut ); + e = k = xc.length; + + // Remove trailing zeros. + for ( ; xc[--k] == 0; xc.pop() ); + if ( !xc[0] ) return '0'; + + if ( i < 0 ) { + --e; + } else { + x.c = xc; + x.e = e; + + // sign is needed for correct rounding. + x.s = sign; + x = div( x, y, dp, rm, baseOut ); + xc = x.c; + r = x.r; + e = x.e; + } + + d = e + dp + 1; + + // The rounding digit, i.e. the digit to the right of the digit that may be rounded up. + i = xc[d]; + k = baseOut / 2; + r = r || d < 0 || xc[d + 1] != null; + + r = rm < 4 ? ( i != null || r ) && ( rm == 0 || rm == ( x.s < 0 ? 3 : 2 ) ) + : i > k || i == k &&( rm == 4 || r || rm == 6 && xc[d - 1] & 1 || + rm == ( x.s < 0 ? 8 : 7 ) ); + + if ( d < 1 || !xc[0] ) { + + // 1^-dp or 0. + str = r ? toFixedPoint( '1', -dp ) : '0'; + } else { + xc.length = d; + + if (r) { + + // Rounding up may mean the previous digit has to be rounded up and so on. + for ( --baseOut; ++xc[--d] > baseOut; ) { + xc[d] = 0; + + if ( !d ) { + ++e; + xc.unshift(1); + } + } + } + + // Determine trailing zeros. + for ( k = xc.length; !xc[--k]; ); + + // E.g. [4, 11, 15] becomes 4bf. + for ( i = 0, str = ''; i <= k; str += ALPHABET.charAt( xc[i++] ) ); + str = toFixedPoint( str, e ); + } + + // The caller will add the sign. + return str; + } + + + // Perform division in the specified base. Called by div and convertBase. + div = (function () { + + // Assume non-zero x and k. + function multiply( x, k, base ) { + var m, temp, xlo, xhi, + carry = 0, + i = x.length, + klo = k % SQRT_BASE, + khi = k / SQRT_BASE | 0; + + for ( x = x.slice(); i--; ) { + xlo = x[i] % SQRT_BASE; + xhi = x[i] / SQRT_BASE | 0; + m = khi * xlo + xhi * klo; + temp = klo * xlo + ( ( m % SQRT_BASE ) * SQRT_BASE ) + carry; + carry = ( temp / base | 0 ) + ( m / SQRT_BASE | 0 ) + khi * xhi; + x[i] = temp % base; + } + + if (carry) x.unshift(carry); + + return x; + } + + function compare( a, b, aL, bL ) { + var i, cmp; + + if ( aL != bL ) { + cmp = aL > bL ? 1 : -1; + } else { + + for ( i = cmp = 0; i < aL; i++ ) { + + if ( a[i] != b[i] ) { + cmp = a[i] > b[i] ? 1 : -1; + break; + } + } + } + return cmp; + } + + function subtract( a, b, aL, base ) { + var i = 0; + + // Subtract b from a. + for ( ; aL--; ) { + a[aL] -= i; + i = a[aL] < b[aL] ? 1 : 0; + a[aL] = i * base + a[aL] - b[aL]; + } + + // Remove leading zeros. + for ( ; !a[0] && a.length > 1; a.shift() ); + } + + // x: dividend, y: divisor. + return function ( x, y, dp, rm, base ) { + var cmp, e, i, more, n, prod, prodL, q, qc, rem, remL, rem0, xi, xL, yc0, + yL, yz, + s = x.s == y.s ? 1 : -1, + xc = x.c, + yc = y.c; + + // Either NaN, Infinity or 0? + if ( !xc || !xc[0] || !yc || !yc[0] ) { + + return new BigNumber( + + // Return NaN if either NaN, or both Infinity or 0. + !x.s || !y.s || ( xc ? yc && xc[0] == yc[0] : !yc ) ? NaN : + + // Return ±0 if x is ±0 or y is ±Infinity, or return ±Infinity as y is ±0. + xc && xc[0] == 0 || !yc ? s * 0 : s / 0 + ); + } + + q = new BigNumber(s); + qc = q.c = []; + e = x.e - y.e; + s = dp + e + 1; + + if ( !base ) { + base = BASE; + e = bitFloor( x.e / LOG_BASE ) - bitFloor( y.e / LOG_BASE ); + s = s / LOG_BASE | 0; + } + + // Result exponent may be one less then the current value of e. + // The coefficients of the BigNumbers from convertBase may have trailing zeros. + for ( i = 0; yc[i] == ( xc[i] || 0 ); i++ ); + if ( yc[i] > ( xc[i] || 0 ) ) e--; + + if ( s < 0 ) { + qc.push(1); + more = true; + } else { + xL = xc.length; + yL = yc.length; + i = 0; + s += 2; + + // Normalise xc and yc so highest order digit of yc is >= base/2 + + n = mathfloor( base / ( yc[0] + 1 ) ); + + if ( n > 1 ) { + yc = multiply( yc, n, base ); + xc = multiply( xc, n, base ); + yL = yc.length; + xL = xc.length; + } + + xi = yL; + rem = xc.slice( 0, yL ); + remL = rem.length; + + // Add zeros to make remainder as long as divisor. + for ( ; remL < yL; rem[remL++] = 0 ); + yz = yc.slice(); + yz.unshift(0); + yc0 = yc[0]; + if ( yc[1] >= base / 2 ) yc0++; + + do { + n = 0; + + // Compare divisor and remainder. + cmp = compare( yc, rem, yL, remL ); + + // If divisor < remainder. + if ( cmp < 0 ) { + + // Calculate trial digit, n. + + rem0 = rem[0]; + if ( yL != remL ) rem0 = rem0 * base + ( rem[1] || 0 ); + + // n is how many times the divisor goes into the current remainder. + n = mathfloor( rem0 / yc0 ); + + // Algorithm: + // 1. product = divisor * trial digit (n) + // 2. if product > remainder: product -= divisor, n-- + // 3. remainder -= product + // 4. if product was < remainder at 2: + // 5. compare new remainder and divisor + // 6. If remainder > divisor: remainder -= divisor, n++ + + if ( n > 1 ) { + if ( n >= base ) n = base - 1; + + // product = divisor * trial digit. + prod = multiply( yc, n, base ); + prodL = prod.length; + remL = rem.length; + + // Compare product and remainder. + cmp = compare( prod, rem, prodL, remL ); + + // product > remainder. + if ( cmp == 1 ) { + n--; + + // Subtract divisor from product. + subtract( prod, yL < prodL ? yz : yc, prodL, base ); + } + } else { + + // cmp is -1. + // If n is 0, there is no need to compare yc and rem again + // below, so change cmp to 1 to avoid it. + // If n is 1, compare yc and rem again below. + if ( n == 0 ) cmp = n = 1; + prod = yc.slice(); + } + + prodL = prod.length; + if ( prodL < remL ) prod.unshift(0); + + // Subtract product from remainder. + subtract( rem, prod, remL, base ); + + // If product was < previous remainder. + if ( cmp == -1 ) { + remL = rem.length; + + // Compare divisor and new remainder. + cmp = compare( yc, rem, yL, remL ); + + // If divisor < new remainder, subtract divisor from remainder. + if ( cmp < 1 ) { + n++; + + // Subtract divisor from remainder. + subtract( rem, yL < remL ? yz : yc, remL, base ); + } + } + remL = rem.length; + } else if ( cmp === 0 ) { + n++; + rem = [0]; + } + // if cmp === 1, n will be 0 + + // Add the next digit, n, to the result array. + qc[i++] = n; + + // Update the remainder. + if ( cmp && rem[0] ) { + rem[remL++] = xc[xi] || 0; + } else { + rem = [ xc[xi] ]; + remL = 1; + } + } while ( ( xi++ < xL || rem[0] != null ) && s-- ); + + more = rem[0] != null; + + // Leading zero? + if ( !qc[0] ) qc.shift(); + } + + if ( base == BASE ) { + + // To calculate q.e, first get the number of digits of qc[0]. + for ( i = 1, s = qc[0]; s >= 10; s /= 10, i++ ); + round( q, dp + ( q.e = i + e * LOG_BASE - 1 ) + 1, rm, more ); + + // Caller is convertBase. + } else { + q.e = e; + q.r = +more; + } + + return q; + }; + })(); + + + /* + * Return a string representing the value of BigNumber n in fixed-point or exponential + * notation rounded to the specified decimal places or significant digits. + * + * n is a BigNumber. + * i is the index of the last digit required (i.e. the digit that may be rounded up). + * rm is the rounding mode. + * caller is caller id: toExponential 19, toFixed 20, toFormat 21, toPrecision 24. + */ + function format( n, i, rm, caller ) { + var c0, e, ne, len, str; + + rm = rm != null && isValidInt( rm, 0, 8, caller, roundingMode ) + ? rm | 0 : ROUNDING_MODE; + + if ( !n.c ) return n.toString(); + c0 = n.c[0]; + ne = n.e; + + if ( i == null ) { + str = coeffToString( n.c ); + str = caller == 19 || caller == 24 && ne <= TO_EXP_NEG + ? toExponential( str, ne ) + : toFixedPoint( str, ne ); + } else { + n = round( new BigNumber(n), i, rm ); + + // n.e may have changed if the value was rounded up. + e = n.e; + + str = coeffToString( n.c ); + len = str.length; + + // toPrecision returns exponential notation if the number of significant digits + // specified is less than the number of digits necessary to represent the integer + // part of the value in fixed-point notation. + + // Exponential notation. + if ( caller == 19 || caller == 24 && ( i <= e || e <= TO_EXP_NEG ) ) { + + // Append zeros? + for ( ; len < i; str += '0', len++ ); + str = toExponential( str, e ); + + // Fixed-point notation. + } else { + i -= ne; + str = toFixedPoint( str, e ); + + // Append zeros? + if ( e + 1 > len ) { + if ( --i > 0 ) for ( str += '.'; i--; str += '0' ); + } else { + i += e - len; + if ( i > 0 ) { + if ( e + 1 == len ) str += '.'; + for ( ; i--; str += '0' ); + } + } + } + } + + return n.s < 0 && c0 ? '-' + str : str; + } + + + // Handle BigNumber.max and BigNumber.min. + function maxOrMin( args, method ) { + var m, n, + i = 0; + + if ( isArray( args[0] ) ) args = args[0]; + m = new BigNumber( args[0] ); + + for ( ; ++i < args.length; ) { + n = new BigNumber( args[i] ); + + // If any number is NaN, return NaN. + if ( !n.s ) { + m = n; + break; + } else if ( method.call( m, n ) ) { + m = n; + } + } + + return m; + } + + + /* + * Return true if n is an integer in range, otherwise throw. + * Use for argument validation when ERRORS is true. + */ + function intValidatorWithErrors( n, min, max, caller, name ) { + if ( n < min || n > max || n != truncate(n) ) { + raise( caller, ( name || 'decimal places' ) + + ( n < min || n > max ? ' out of range' : ' not an integer' ), n ); + } + + return true; + } + + + /* + * Strip trailing zeros, calculate base 10 exponent and check against MIN_EXP and MAX_EXP. + * Called by minus, plus and times. + */ + function normalise( n, c, e ) { + var i = 1, + j = c.length; + + // Remove trailing zeros. + for ( ; !c[--j]; c.pop() ); + + // Calculate the base 10 exponent. First get the number of digits of c[0]. + for ( j = c[0]; j >= 10; j /= 10, i++ ); + + // Overflow? + if ( ( e = i + e * LOG_BASE - 1 ) > MAX_EXP ) { + + // Infinity. + n.c = n.e = null; + + // Underflow? + } else if ( e < MIN_EXP ) { + + // Zero. + n.c = [ n.e = 0 ]; + } else { + n.e = e; + n.c = c; + } + + return n; + } + + + // Handle values that fail the validity test in BigNumber. + parseNumeric = (function () { + var basePrefix = /^(-?)0([xbo])(?=\w[\w.]*$)/i, + dotAfter = /^([^.]+)\.$/, + dotBefore = /^\.([^.]+)$/, + isInfinityOrNaN = /^-?(Infinity|NaN)$/, + whitespaceOrPlus = /^\s*\+(?=[\w.])|^\s+|\s+$/g; + + return function ( x, str, num, b ) { + var base, + s = num ? str : str.replace( whitespaceOrPlus, '' ); + + // No exception on ±Infinity or NaN. + if ( isInfinityOrNaN.test(s) ) { + x.s = isNaN(s) ? null : s < 0 ? -1 : 1; + } else { + if ( !num ) { + + // basePrefix = /^(-?)0([xbo])(?=\w[\w.]*$)/i + s = s.replace( basePrefix, function ( m, p1, p2 ) { + base = ( p2 = p2.toLowerCase() ) == 'x' ? 16 : p2 == 'b' ? 2 : 8; + return !b || b == base ? p1 : m; + }); + + if (b) { + base = b; + + // E.g. '1.' to '1', '.1' to '0.1' + s = s.replace( dotAfter, '$1' ).replace( dotBefore, '0.$1' ); + } + + if ( str != s ) return new BigNumber( s, base ); + } + + // 'new BigNumber() not a number: {n}' + // 'new BigNumber() not a base {b} number: {n}' + if (ERRORS) raise( id, 'not a' + ( b ? ' base ' + b : '' ) + ' number', str ); + x.s = null; + } + + x.c = x.e = null; + id = 0; + } + })(); + + + // Throw a BigNumber Error. + function raise( caller, msg, val ) { + var error = new Error( [ + 'new BigNumber', // 0 + 'cmp', // 1 + 'config', // 2 + 'div', // 3 + 'divToInt', // 4 + 'eq', // 5 + 'gt', // 6 + 'gte', // 7 + 'lt', // 8 + 'lte', // 9 + 'minus', // 10 + 'mod', // 11 + 'plus', // 12 + 'precision', // 13 + 'random', // 14 + 'round', // 15 + 'shift', // 16 + 'times', // 17 + 'toDigits', // 18 + 'toExponential', // 19 + 'toFixed', // 20 + 'toFormat', // 21 + 'toFraction', // 22 + 'pow', // 23 + 'toPrecision', // 24 + 'toString', // 25 + 'BigNumber' // 26 + ][caller] + '() ' + msg + ': ' + val ); + + error.name = 'BigNumber Error'; + id = 0; + throw error; + } + + + /* + * Round x to sd significant digits using rounding mode rm. Check for over/under-flow. + * If r is truthy, it is known that there are more digits after the rounding digit. + */ + function round( x, sd, rm, r ) { + var d, i, j, k, n, ni, rd, + xc = x.c, + pows10 = POWS_TEN; + + // if x is not Infinity or NaN... + if (xc) { + + // rd is the rounding digit, i.e. the digit after the digit that may be rounded up. + // n is a base 1e14 number, the value of the element of array x.c containing rd. + // ni is the index of n within x.c. + // d is the number of digits of n. + // i is the index of rd within n including leading zeros. + // j is the actual index of rd within n (if < 0, rd is a leading zero). + out: { + + // Get the number of digits of the first element of xc. + for ( d = 1, k = xc[0]; k >= 10; k /= 10, d++ ); + i = sd - d; + + // If the rounding digit is in the first element of xc... + if ( i < 0 ) { + i += LOG_BASE; + j = sd; + n = xc[ ni = 0 ]; + + // Get the rounding digit at index j of n. + rd = n / pows10[ d - j - 1 ] % 10 | 0; + } else { + ni = mathceil( ( i + 1 ) / LOG_BASE ); + + if ( ni >= xc.length ) { + + if (r) { + + // Needed by sqrt. + for ( ; xc.length <= ni; xc.push(0) ); + n = rd = 0; + d = 1; + i %= LOG_BASE; + j = i - LOG_BASE + 1; + } else { + break out; + } + } else { + n = k = xc[ni]; + + // Get the number of digits of n. + for ( d = 1; k >= 10; k /= 10, d++ ); + + // Get the index of rd within n. + i %= LOG_BASE; + + // Get the index of rd within n, adjusted for leading zeros. + // The number of leading zeros of n is given by LOG_BASE - d. + j = i - LOG_BASE + d; + + // Get the rounding digit at index j of n. + rd = j < 0 ? 0 : n / pows10[ d - j - 1 ] % 10 | 0; + } + } + + r = r || sd < 0 || + + // Are there any non-zero digits after the rounding digit? + // The expression n % pows10[ d - j - 1 ] returns all digits of n to the right + // of the digit at j, e.g. if n is 908714 and j is 2, the expression gives 714. + xc[ni + 1] != null || ( j < 0 ? n : n % pows10[ d - j - 1 ] ); + + r = rm < 4 + ? ( rd || r ) && ( rm == 0 || rm == ( x.s < 0 ? 3 : 2 ) ) + : rd > 5 || rd == 5 && ( rm == 4 || r || rm == 6 && + + // Check whether the digit to the left of the rounding digit is odd. + ( ( i > 0 ? j > 0 ? n / pows10[ d - j ] : 0 : xc[ni - 1] ) % 10 ) & 1 || + rm == ( x.s < 0 ? 8 : 7 ) ); + + if ( sd < 1 || !xc[0] ) { + xc.length = 0; + + if (r) { + + // Convert sd to decimal places. + sd -= x.e + 1; + + // 1, 0.1, 0.01, 0.001, 0.0001 etc. + xc[0] = pows10[ sd % LOG_BASE ]; + x.e = -sd || 0; + } else { + + // Zero. + xc[0] = x.e = 0; + } + + return x; + } + + // Remove excess digits. + if ( i == 0 ) { + xc.length = ni; + k = 1; + ni--; + } else { + xc.length = ni + 1; + k = pows10[ LOG_BASE - i ]; + + // E.g. 56700 becomes 56000 if 7 is the rounding digit. + // j > 0 means i > number of leading zeros of n. + xc[ni] = j > 0 ? mathfloor( n / pows10[ d - j ] % pows10[j] ) * k : 0; + } + + // Round up? + if (r) { + + for ( ; ; ) { + + // If the digit to be rounded up is in the first element of xc... + if ( ni == 0 ) { + + // i will be the length of xc[0] before k is added. + for ( i = 1, j = xc[0]; j >= 10; j /= 10, i++ ); + j = xc[0] += k; + for ( k = 1; j >= 10; j /= 10, k++ ); + + // if i != k the length has increased. + if ( i != k ) { + x.e++; + if ( xc[0] == BASE ) xc[0] = 1; + } + + break; + } else { + xc[ni] += k; + if ( xc[ni] != BASE ) break; + xc[ni--] = 0; + k = 1; + } + } + } + + // Remove trailing zeros. + for ( i = xc.length; xc[--i] === 0; xc.pop() ); + } + + // Overflow? Infinity. + if ( x.e > MAX_EXP ) { + x.c = x.e = null; + + // Underflow? Zero. + } else if ( x.e < MIN_EXP ) { + x.c = [ x.e = 0 ]; + } + } + + return x; + } + + + // PROTOTYPE/INSTANCE METHODS + + + /* + * Return a new BigNumber whose value is the absolute value of this BigNumber. + */ + P.absoluteValue = P.abs = function () { + var x = new BigNumber(this); + if ( x.s < 0 ) x.s = 1; + return x; + }; + + + /* + * Return a new BigNumber whose value is the value of this BigNumber rounded to a whole + * number in the direction of Infinity. + */ + P.ceil = function () { + return round( new BigNumber(this), this.e + 1, 2 ); + }; + + + /* + * Return + * 1 if the value of this BigNumber is greater than the value of BigNumber(y, b), + * -1 if the value of this BigNumber is less than the value of BigNumber(y, b), + * 0 if they have the same value, + * or null if the value of either is NaN. + */ + P.comparedTo = P.cmp = function ( y, b ) { + id = 1; + return compare( this, new BigNumber( y, b ) ); + }; + + + /* + * Return the number of decimal places of the value of this BigNumber, or null if the value + * of this BigNumber is ±Infinity or NaN. + */ + P.decimalPlaces = P.dp = function () { + var n, v, + c = this.c; + + if ( !c ) return null; + n = ( ( v = c.length - 1 ) - bitFloor( this.e / LOG_BASE ) ) * LOG_BASE; + + // Subtract the number of trailing zeros of the last number. + if ( v = c[v] ) for ( ; v % 10 == 0; v /= 10, n-- ); + if ( n < 0 ) n = 0; + + return n; + }; + + + /* + * n / 0 = I + * n / N = N + * n / I = 0 + * 0 / n = 0 + * 0 / 0 = N + * 0 / N = N + * 0 / I = 0 + * N / n = N + * N / 0 = N + * N / N = N + * N / I = N + * I / n = I + * I / 0 = I + * I / N = N + * I / I = N + * + * Return a new BigNumber whose value is the value of this BigNumber divided by the value of + * BigNumber(y, b), rounded according to DECIMAL_PLACES and ROUNDING_MODE. + */ + P.dividedBy = P.div = function ( y, b ) { + id = 3; + return div( this, new BigNumber( y, b ), DECIMAL_PLACES, ROUNDING_MODE ); + }; + + + /* + * Return a new BigNumber whose value is the integer part of dividing the value of this + * BigNumber by the value of BigNumber(y, b). + */ + P.dividedToIntegerBy = P.divToInt = function ( y, b ) { + id = 4; + return div( this, new BigNumber( y, b ), 0, 1 ); + }; + + + /* + * Return true if the value of this BigNumber is equal to the value of BigNumber(y, b), + * otherwise returns false. + */ + P.equals = P.eq = function ( y, b ) { + id = 5; + return compare( this, new BigNumber( y, b ) ) === 0; + }; + + + /* + * Return a new BigNumber whose value is the value of this BigNumber rounded to a whole + * number in the direction of -Infinity. + */ + P.floor = function () { + return round( new BigNumber(this), this.e + 1, 3 ); + }; + + + /* + * Return true if the value of this BigNumber is greater than the value of BigNumber(y, b), + * otherwise returns false. + */ + P.greaterThan = P.gt = function ( y, b ) { + id = 6; + return compare( this, new BigNumber( y, b ) ) > 0; + }; + + + /* + * Return true if the value of this BigNumber is greater than or equal to the value of + * BigNumber(y, b), otherwise returns false. + */ + P.greaterThanOrEqualTo = P.gte = function ( y, b ) { + id = 7; + return ( b = compare( this, new BigNumber( y, b ) ) ) === 1 || b === 0; + + }; + + + /* + * Return true if the value of this BigNumber is a finite number, otherwise returns false. + */ + P.isFinite = function () { + return !!this.c; + }; + + + /* + * Return true if the value of this BigNumber is an integer, otherwise return false. + */ + P.isInteger = P.isInt = function () { + return !!this.c && bitFloor( this.e / LOG_BASE ) > this.c.length - 2; + }; + + + /* + * Return true if the value of this BigNumber is NaN, otherwise returns false. + */ + P.isNaN = function () { + return !this.s; + }; + + + /* + * Return true if the value of this BigNumber is negative, otherwise returns false. + */ + P.isNegative = P.isNeg = function () { + return this.s < 0; + }; + + + /* + * Return true if the value of this BigNumber is 0 or -0, otherwise returns false. + */ + P.isZero = function () { + return !!this.c && this.c[0] == 0; + }; + + + /* + * Return true if the value of this BigNumber is less than the value of BigNumber(y, b), + * otherwise returns false. + */ + P.lessThan = P.lt = function ( y, b ) { + id = 8; + return compare( this, new BigNumber( y, b ) ) < 0; + }; + + + /* + * Return true if the value of this BigNumber is less than or equal to the value of + * BigNumber(y, b), otherwise returns false. + */ + P.lessThanOrEqualTo = P.lte = function ( y, b ) { + id = 9; + return ( b = compare( this, new BigNumber( y, b ) ) ) === -1 || b === 0; + }; + + + /* + * n - 0 = n + * n - N = N + * n - I = -I + * 0 - n = -n + * 0 - 0 = 0 + * 0 - N = N + * 0 - I = -I + * N - n = N + * N - 0 = N + * N - N = N + * N - I = N + * I - n = I + * I - 0 = I + * I - N = N + * I - I = N + * + * Return a new BigNumber whose value is the value of this BigNumber minus the value of + * BigNumber(y, b). + */ + P.minus = P.sub = function ( y, b ) { + var i, j, t, xLTy, + x = this, + a = x.s; + + id = 10; + y = new BigNumber( y, b ); + b = y.s; + + // Either NaN? + if ( !a || !b ) return new BigNumber(NaN); + + // Signs differ? + if ( a != b ) { + y.s = -b; + return x.plus(y); + } + + var xe = x.e / LOG_BASE, + ye = y.e / LOG_BASE, + xc = x.c, + yc = y.c; + + if ( !xe || !ye ) { + + // Either Infinity? + if ( !xc || !yc ) return xc ? ( y.s = -b, y ) : new BigNumber( yc ? x : NaN ); + + // Either zero? + if ( !xc[0] || !yc[0] ) { + + // Return y if y is non-zero, x if x is non-zero, or zero if both are zero. + return yc[0] ? ( y.s = -b, y ) : new BigNumber( xc[0] ? x : + + // IEEE 754 (2008) 6.3: n - n = -0 when rounding to -Infinity + ROUNDING_MODE == 3 ? -0 : 0 ); + } + } + + xe = bitFloor(xe); + ye = bitFloor(ye); + xc = xc.slice(); + + // Determine which is the bigger number. + if ( a = xe - ye ) { + + if ( xLTy = a < 0 ) { + a = -a; + t = xc; + } else { + ye = xe; + t = yc; + } + + t.reverse(); + + // Prepend zeros to equalise exponents. + for ( b = a; b--; t.push(0) ); + t.reverse(); + } else { + + // Exponents equal. Check digit by digit. + j = ( xLTy = ( a = xc.length ) < ( b = yc.length ) ) ? a : b; + + for ( a = b = 0; b < j; b++ ) { + + if ( xc[b] != yc[b] ) { + xLTy = xc[b] < yc[b]; + break; + } + } + } + + // x < y? Point xc to the array of the bigger number. + if (xLTy) t = xc, xc = yc, yc = t, y.s = -y.s; + + b = ( j = yc.length ) - ( i = xc.length ); + + // Append zeros to xc if shorter. + // No need to add zeros to yc if shorter as subtract only needs to start at yc.length. + if ( b > 0 ) for ( ; b--; xc[i++] = 0 ); + b = BASE - 1; + + // Subtract yc from xc. + for ( ; j > a; ) { + + if ( xc[--j] < yc[j] ) { + for ( i = j; i && !xc[--i]; xc[i] = b ); + --xc[i]; + xc[j] += BASE; + } + + xc[j] -= yc[j]; + } + + // Remove leading zeros and adjust exponent accordingly. + for ( ; xc[0] == 0; xc.shift(), --ye ); + + // Zero? + if ( !xc[0] ) { + + // Following IEEE 754 (2008) 6.3, + // n - n = +0 but n - n = -0 when rounding towards -Infinity. + y.s = ROUNDING_MODE == 3 ? -1 : 1; + y.c = [ y.e = 0 ]; + return y; + } + + // No need to check for Infinity as +x - +y != Infinity && -x - -y != Infinity + // for finite x and y. + return normalise( y, xc, ye ); + }; + + + /* + * n % 0 = N + * n % N = N + * n % I = n + * 0 % n = 0 + * -0 % n = -0 + * 0 % 0 = N + * 0 % N = N + * 0 % I = 0 + * N % n = N + * N % 0 = N + * N % N = N + * N % I = N + * I % n = N + * I % 0 = N + * I % N = N + * I % I = N + * + * Return a new BigNumber whose value is the value of this BigNumber modulo the value of + * BigNumber(y, b). The result depends on the value of MODULO_MODE. + */ + P.modulo = P.mod = function ( y, b ) { + var q, s, + x = this; + + id = 11; + y = new BigNumber( y, b ); + + // Return NaN if x is Infinity or NaN, or y is NaN or zero. + if ( !x.c || !y.s || y.c && !y.c[0] ) { + return new BigNumber(NaN); + + // Return x if y is Infinity or x is zero. + } else if ( !y.c || x.c && !x.c[0] ) { + return new BigNumber(x); + } + + if ( MODULO_MODE == 9 ) { + + // Euclidian division: q = sign(y) * floor(x / abs(y)) + // r = x - qy where 0 <= r < abs(y) + s = y.s; + y.s = 1; + q = div( x, y, 0, 3 ); + y.s = s; + q.s *= s; + } else { + q = div( x, y, 0, MODULO_MODE ); + } + + return x.minus( q.times(y) ); + }; + + + /* + * Return a new BigNumber whose value is the value of this BigNumber negated, + * i.e. multiplied by -1. + */ + P.negated = P.neg = function () { + var x = new BigNumber(this); + x.s = -x.s || null; + return x; + }; + + + /* + * n + 0 = n + * n + N = N + * n + I = I + * 0 + n = n + * 0 + 0 = 0 + * 0 + N = N + * 0 + I = I + * N + n = N + * N + 0 = N + * N + N = N + * N + I = N + * I + n = I + * I + 0 = I + * I + N = N + * I + I = I + * + * Return a new BigNumber whose value is the value of this BigNumber plus the value of + * BigNumber(y, b). + */ + P.plus = P.add = function ( y, b ) { + var t, + x = this, + a = x.s; + + id = 12; + y = new BigNumber( y, b ); + b = y.s; + + // Either NaN? + if ( !a || !b ) return new BigNumber(NaN); + + // Signs differ? + if ( a != b ) { + y.s = -b; + return x.minus(y); + } + + var xe = x.e / LOG_BASE, + ye = y.e / LOG_BASE, + xc = x.c, + yc = y.c; + + if ( !xe || !ye ) { + + // Return ±Infinity if either ±Infinity. + if ( !xc || !yc ) return new BigNumber( a / 0 ); + + // Either zero? + // Return y if y is non-zero, x if x is non-zero, or zero if both are zero. + if ( !xc[0] || !yc[0] ) return yc[0] ? y : new BigNumber( xc[0] ? x : a * 0 ); + } + + xe = bitFloor(xe); + ye = bitFloor(ye); + xc = xc.slice(); + + // Prepend zeros to equalise exponents. Faster to use reverse then do unshifts. + if ( a = xe - ye ) { + if ( a > 0 ) { + ye = xe; + t = yc; + } else { + a = -a; + t = xc; + } + + t.reverse(); + for ( ; a--; t.push(0) ); + t.reverse(); + } + + a = xc.length; + b = yc.length; + + // Point xc to the longer array, and b to the shorter length. + if ( a - b < 0 ) t = yc, yc = xc, xc = t, b = a; + + // Only start adding at yc.length - 1 as the further digits of xc can be ignored. + for ( a = 0; b; ) { + a = ( xc[--b] = xc[b] + yc[b] + a ) / BASE | 0; + xc[b] %= BASE; + } + + if (a) { + xc.unshift(a); + ++ye; + } + + // No need to check for zero, as +x + +y != 0 && -x + -y != 0 + // ye = MAX_EXP + 1 possible + return normalise( y, xc, ye ); + }; + + + /* + * Return the number of significant digits of the value of this BigNumber. + * + * [z] {boolean|number} Whether to count integer-part trailing zeros: true, false, 1 or 0. + */ + P.precision = P.sd = function (z) { + var n, v, + x = this, + c = x.c; + + // 'precision() argument not a boolean or binary digit: {z}' + if ( z != null && z !== !!z && z !== 1 && z !== 0 ) { + if (ERRORS) raise( 13, 'argument' + notBool, z ); + if ( z != !!z ) z = null; + } + + if ( !c ) return null; + v = c.length - 1; + n = v * LOG_BASE + 1; + + if ( v = c[v] ) { + + // Subtract the number of trailing zeros of the last element. + for ( ; v % 10 == 0; v /= 10, n-- ); + + // Add the number of digits of the first element. + for ( v = c[0]; v >= 10; v /= 10, n++ ); + } + + if ( z && x.e + 1 > n ) n = x.e + 1; + + return n; + }; + + + /* + * Return a new BigNumber whose value is the value of this BigNumber rounded to a maximum of + * dp decimal places using rounding mode rm, or to 0 and ROUNDING_MODE respectively if + * omitted. + * + * [dp] {number} Decimal places. Integer, 0 to MAX inclusive. + * [rm] {number} Rounding mode. Integer, 0 to 8 inclusive. + * + * 'round() decimal places out of range: {dp}' + * 'round() decimal places not an integer: {dp}' + * 'round() rounding mode not an integer: {rm}' + * 'round() rounding mode out of range: {rm}' + */ + P.round = function ( dp, rm ) { + var n = new BigNumber(this); + + if ( dp == null || isValidInt( dp, 0, MAX, 15 ) ) { + round( n, ~~dp + this.e + 1, rm == null || + !isValidInt( rm, 0, 8, 15, roundingMode ) ? ROUNDING_MODE : rm | 0 ); + } + + return n; + }; + + + /* + * Return a new BigNumber whose value is the value of this BigNumber shifted by k places + * (powers of 10). Shift to the right if n > 0, and to the left if n < 0. + * + * k {number} Integer, -MAX_SAFE_INTEGER to MAX_SAFE_INTEGER inclusive. + * + * If k is out of range and ERRORS is false, the result will be ±0 if k < 0, or ±Infinity + * otherwise. + * + * 'shift() argument not an integer: {k}' + * 'shift() argument out of range: {k}' + */ + P.shift = function (k) { + var n = this; + return isValidInt( k, -MAX_SAFE_INTEGER, MAX_SAFE_INTEGER, 16, 'argument' ) + + // k < 1e+21, or truncate(k) will produce exponential notation. + ? n.times( '1e' + truncate(k) ) + : new BigNumber( n.c && n.c[0] && ( k < -MAX_SAFE_INTEGER || k > MAX_SAFE_INTEGER ) + ? n.s * ( k < 0 ? 0 : 1 / 0 ) + : n ); + }; + + + /* + * sqrt(-n) = N + * sqrt( N) = N + * sqrt(-I) = N + * sqrt( I) = I + * sqrt( 0) = 0 + * sqrt(-0) = -0 + * + * Return a new BigNumber whose value is the square root of the value of this BigNumber, + * rounded according to DECIMAL_PLACES and ROUNDING_MODE. + */ + P.squareRoot = P.sqrt = function () { + var m, n, r, rep, t, + x = this, + c = x.c, + s = x.s, + e = x.e, + dp = DECIMAL_PLACES + 4, + half = new BigNumber('0.5'); + + // Negative/NaN/Infinity/zero? + if ( s !== 1 || !c || !c[0] ) { + return new BigNumber( !s || s < 0 && ( !c || c[0] ) ? NaN : c ? x : 1 / 0 ); + } + + // Initial estimate. + s = Math.sqrt( +x ); + + // Math.sqrt underflow/overflow? + // Pass x to Math.sqrt as integer, then adjust the exponent of the result. + if ( s == 0 || s == 1 / 0 ) { + n = coeffToString(c); + if ( ( n.length + e ) % 2 == 0 ) n += '0'; + s = Math.sqrt(n); + e = bitFloor( ( e + 1 ) / 2 ) - ( e < 0 || e % 2 ); + + if ( s == 1 / 0 ) { + n = '1e' + e; + } else { + n = s.toExponential(); + n = n.slice( 0, n.indexOf('e') + 1 ) + e; + } + + r = new BigNumber(n); + } else { + r = new BigNumber( s + '' ); + } + + // Check for zero. + // r could be zero if MIN_EXP is changed after the this value was created. + // This would cause a division by zero (x/t) and hence Infinity below, which would cause + // coeffToString to throw. + if ( r.c[0] ) { + e = r.e; + s = e + dp; + if ( s < 3 ) s = 0; + + // Newton-Raphson iteration. + for ( ; ; ) { + t = r; + r = half.times( t.plus( div( x, t, dp, 1 ) ) ); + + if ( coeffToString( t.c ).slice( 0, s ) === ( n = + coeffToString( r.c ) ).slice( 0, s ) ) { + + // The exponent of r may here be one less than the final result exponent, + // e.g 0.0009999 (e-4) --> 0.001 (e-3), so adjust s so the rounding digits + // are indexed correctly. + if ( r.e < e ) --s; + n = n.slice( s - 3, s + 1 ); + + // The 4th rounding digit may be in error by -1 so if the 4 rounding digits + // are 9999 or 4999 (i.e. approaching a rounding boundary) continue the + // iteration. + if ( n == '9999' || !rep && n == '4999' ) { + + // On the first iteration only, check to see if rounding up gives the + // exact result as the nines may infinitely repeat. + if ( !rep ) { + round( t, t.e + DECIMAL_PLACES + 2, 0 ); + + if ( t.times(t).eq(x) ) { + r = t; + break; + } + } + + dp += 4; + s += 4; + rep = 1; + } else { + + // If rounding digits are null, 0{0,4} or 50{0,3}, check for exact + // result. If not, then there are further digits and m will be truthy. + if ( !+n || !+n.slice(1) && n.charAt(0) == '5' ) { + + // Truncate to the first rounding digit. + round( r, r.e + DECIMAL_PLACES + 2, 1 ); + m = !r.times(r).eq(x); + } + + break; + } + } + } + } + + return round( r, r.e + DECIMAL_PLACES + 1, ROUNDING_MODE, m ); + }; + + + /* + * n * 0 = 0 + * n * N = N + * n * I = I + * 0 * n = 0 + * 0 * 0 = 0 + * 0 * N = N + * 0 * I = N + * N * n = N + * N * 0 = N + * N * N = N + * N * I = N + * I * n = I + * I * 0 = N + * I * N = N + * I * I = I + * + * Return a new BigNumber whose value is the value of this BigNumber times the value of + * BigNumber(y, b). + */ + P.times = P.mul = function ( y, b ) { + var c, e, i, j, k, m, xcL, xlo, xhi, ycL, ylo, yhi, zc, + base, sqrtBase, + x = this, + xc = x.c, + yc = ( id = 17, y = new BigNumber( y, b ) ).c; + + // Either NaN, ±Infinity or ±0? + if ( !xc || !yc || !xc[0] || !yc[0] ) { + + // Return NaN if either is NaN, or one is 0 and the other is Infinity. + if ( !x.s || !y.s || xc && !xc[0] && !yc || yc && !yc[0] && !xc ) { + y.c = y.e = y.s = null; + } else { + y.s *= x.s; + + // Return ±Infinity if either is ±Infinity. + if ( !xc || !yc ) { + y.c = y.e = null; + + // Return ±0 if either is ±0. + } else { + y.c = [0]; + y.e = 0; + } + } + + return y; + } + + e = bitFloor( x.e / LOG_BASE ) + bitFloor( y.e / LOG_BASE ); + y.s *= x.s; + xcL = xc.length; + ycL = yc.length; + + // Ensure xc points to longer array and xcL to its length. + if ( xcL < ycL ) zc = xc, xc = yc, yc = zc, i = xcL, xcL = ycL, ycL = i; + + // Initialise the result array with zeros. + for ( i = xcL + ycL, zc = []; i--; zc.push(0) ); + + base = BASE; + sqrtBase = SQRT_BASE; + + for ( i = ycL; --i >= 0; ) { + c = 0; + ylo = yc[i] % sqrtBase; + yhi = yc[i] / sqrtBase | 0; + + for ( k = xcL, j = i + k; j > i; ) { + xlo = xc[--k] % sqrtBase; + xhi = xc[k] / sqrtBase | 0; + m = yhi * xlo + xhi * ylo; + xlo = ylo * xlo + ( ( m % sqrtBase ) * sqrtBase ) + zc[j] + c; + c = ( xlo / base | 0 ) + ( m / sqrtBase | 0 ) + yhi * xhi; + zc[j--] = xlo % base; + } + + zc[j] = c; + } + + if (c) { + ++e; + } else { + zc.shift(); + } + + return normalise( y, zc, e ); + }; + + + /* + * Return a new BigNumber whose value is the value of this BigNumber rounded to a maximum of + * sd significant digits using rounding mode rm, or ROUNDING_MODE if rm is omitted. + * + * [sd] {number} Significant digits. Integer, 1 to MAX inclusive. + * [rm] {number} Rounding mode. Integer, 0 to 8 inclusive. + * + * 'toDigits() precision out of range: {sd}' + * 'toDigits() precision not an integer: {sd}' + * 'toDigits() rounding mode not an integer: {rm}' + * 'toDigits() rounding mode out of range: {rm}' + */ + P.toDigits = function ( sd, rm ) { + var n = new BigNumber(this); + sd = sd == null || !isValidInt( sd, 1, MAX, 18, 'precision' ) ? null : sd | 0; + rm = rm == null || !isValidInt( rm, 0, 8, 18, roundingMode ) ? ROUNDING_MODE : rm | 0; + return sd ? round( n, sd, rm ) : n; + }; + + + /* + * Return a string representing the value of this BigNumber in exponential notation and + * rounded using ROUNDING_MODE to dp fixed decimal places. + * + * [dp] {number} Decimal places. Integer, 0 to MAX inclusive. + * [rm] {number} Rounding mode. Integer, 0 to 8 inclusive. + * + * 'toExponential() decimal places not an integer: {dp}' + * 'toExponential() decimal places out of range: {dp}' + * 'toExponential() rounding mode not an integer: {rm}' + * 'toExponential() rounding mode out of range: {rm}' + */ + P.toExponential = function ( dp, rm ) { + return format( this, + dp != null && isValidInt( dp, 0, MAX, 19 ) ? ~~dp + 1 : null, rm, 19 ); + }; + + + /* + * Return a string representing the value of this BigNumber in fixed-point notation rounding + * to dp fixed decimal places using rounding mode rm, or ROUNDING_MODE if rm is omitted. + * + * Note: as with JavaScript's number type, (-0).toFixed(0) is '0', + * but e.g. (-0.00001).toFixed(0) is '-0'. + * + * [dp] {number} Decimal places. Integer, 0 to MAX inclusive. + * [rm] {number} Rounding mode. Integer, 0 to 8 inclusive. + * + * 'toFixed() decimal places not an integer: {dp}' + * 'toFixed() decimal places out of range: {dp}' + * 'toFixed() rounding mode not an integer: {rm}' + * 'toFixed() rounding mode out of range: {rm}' + */ + P.toFixed = function ( dp, rm ) { + return format( this, dp != null && isValidInt( dp, 0, MAX, 20 ) + ? ~~dp + this.e + 1 : null, rm, 20 ); + }; + + + /* + * Return a string representing the value of this BigNumber in fixed-point notation rounded + * using rm or ROUNDING_MODE to dp decimal places, and formatted according to the properties + * of the FORMAT object (see BigNumber.config). + * + * FORMAT = { + * decimalSeparator : '.', + * groupSeparator : ',', + * groupSize : 3, + * secondaryGroupSize : 0, + * fractionGroupSeparator : '\xA0', // non-breaking space + * fractionGroupSize : 0 + * }; + * + * [dp] {number} Decimal places. Integer, 0 to MAX inclusive. + * [rm] {number} Rounding mode. Integer, 0 to 8 inclusive. + * + * 'toFormat() decimal places not an integer: {dp}' + * 'toFormat() decimal places out of range: {dp}' + * 'toFormat() rounding mode not an integer: {rm}' + * 'toFormat() rounding mode out of range: {rm}' + */ + P.toFormat = function ( dp, rm ) { + var str = format( this, dp != null && isValidInt( dp, 0, MAX, 21 ) + ? ~~dp + this.e + 1 : null, rm, 21 ); + + if ( this.c ) { + var i, + arr = str.split('.'), + g1 = +FORMAT.groupSize, + g2 = +FORMAT.secondaryGroupSize, + groupSeparator = FORMAT.groupSeparator, + intPart = arr[0], + fractionPart = arr[1], + isNeg = this.s < 0, + intDigits = isNeg ? intPart.slice(1) : intPart, + len = intDigits.length; + + if (g2) i = g1, g1 = g2, g2 = i, len -= i; + + if ( g1 > 0 && len > 0 ) { + i = len % g1 || g1; + intPart = intDigits.substr( 0, i ); + + for ( ; i < len; i += g1 ) { + intPart += groupSeparator + intDigits.substr( i, g1 ); + } + + if ( g2 > 0 ) intPart += groupSeparator + intDigits.slice(i); + if (isNeg) intPart = '-' + intPart; + } + + str = fractionPart + ? intPart + FORMAT.decimalSeparator + ( ( g2 = +FORMAT.fractionGroupSize ) + ? fractionPart.replace( new RegExp( '\\d{' + g2 + '}\\B', 'g' ), + '$&' + FORMAT.fractionGroupSeparator ) + : fractionPart ) + : intPart; + } + + return str; + }; + + + /* + * Return a string array representing the value of this BigNumber as a simple fraction with + * an integer numerator and an integer denominator. The denominator will be a positive + * non-zero value less than or equal to the specified maximum denominator. If a maximum + * denominator is not specified, the denominator will be the lowest value necessary to + * represent the number exactly. + * + * [md] {number|string|BigNumber} Integer >= 1 and < Infinity. The maximum denominator. + * + * 'toFraction() max denominator not an integer: {md}' + * 'toFraction() max denominator out of range: {md}' + */ + P.toFraction = function (md) { + var arr, d0, d2, e, exp, n, n0, q, s, + k = ERRORS, + x = this, + xc = x.c, + d = new BigNumber(ONE), + n1 = d0 = new BigNumber(ONE), + d1 = n0 = new BigNumber(ONE); + + if ( md != null ) { + ERRORS = false; + n = new BigNumber(md); + ERRORS = k; + + if ( !( k = n.isInt() ) || n.lt(ONE) ) { + + if (ERRORS) { + raise( 22, + 'max denominator ' + ( k ? 'out of range' : 'not an integer' ), md ); + } + + // ERRORS is false: + // If md is a finite non-integer >= 1, round it to an integer and use it. + md = !k && n.c && round( n, n.e + 1, 1 ).gte(ONE) ? n : null; + } + } + + if ( !xc ) return x.toString(); + s = coeffToString(xc); + + // Determine initial denominator. + // d is a power of 10 and the minimum max denominator that specifies the value exactly. + e = d.e = s.length - x.e - 1; + d.c[0] = POWS_TEN[ ( exp = e % LOG_BASE ) < 0 ? LOG_BASE + exp : exp ]; + md = !md || n.cmp(d) > 0 ? ( e > 0 ? d : n1 ) : n; + + exp = MAX_EXP; + MAX_EXP = 1 / 0; + n = new BigNumber(s); + + // n0 = d1 = 0 + n0.c[0] = 0; + + for ( ; ; ) { + q = div( n, d, 0, 1 ); + d2 = d0.plus( q.times(d1) ); + if ( d2.cmp(md) == 1 ) break; + d0 = d1; + d1 = d2; + n1 = n0.plus( q.times( d2 = n1 ) ); + n0 = d2; + d = n.minus( q.times( d2 = d ) ); + n = d2; + } + + d2 = div( md.minus(d0), d1, 0, 1 ); + n0 = n0.plus( d2.times(n1) ); + d0 = d0.plus( d2.times(d1) ); + n0.s = n1.s = x.s; + e *= 2; + + // Determine which fraction is closer to x, n0/d0 or n1/d1 + arr = div( n1, d1, e, ROUNDING_MODE ).minus(x).abs().cmp( + div( n0, d0, e, ROUNDING_MODE ).minus(x).abs() ) < 1 + ? [ n1.toString(), d1.toString() ] + : [ n0.toString(), d0.toString() ]; + + MAX_EXP = exp; + return arr; + }; + + + /* + * Return the value of this BigNumber converted to a number primitive. + */ + P.toNumber = function () { + var x = this; + + // Ensure zero has correct sign. + return +x || ( x.s ? x.s * 0 : NaN ); + }; + + + /* + * Return a BigNumber whose value is the value of this BigNumber raised to the power n. + * If n is negative round according to DECIMAL_PLACES and ROUNDING_MODE. + * If POW_PRECISION is not 0, round to POW_PRECISION using ROUNDING_MODE. + * + * n {number} Integer, -9007199254740992 to 9007199254740992 inclusive. + * (Performs 54 loop iterations for n of 9007199254740992.) + * + * 'pow() exponent not an integer: {n}' + * 'pow() exponent out of range: {n}' + */ + P.toPower = P.pow = function (n) { + var k, y, + i = mathfloor( n < 0 ? -n : +n ), + x = this; + + // Pass ±Infinity to Math.pow if exponent is out of range. + if ( !isValidInt( n, -MAX_SAFE_INTEGER, MAX_SAFE_INTEGER, 23, 'exponent' ) && + ( !isFinite(n) || i > MAX_SAFE_INTEGER && ( n /= 0 ) || + parseFloat(n) != n && !( n = NaN ) ) ) { + return new BigNumber( Math.pow( +x, n ) ); + } + + // Truncating each coefficient array to a length of k after each multiplication equates + // to truncating significant digits to POW_PRECISION + [28, 41], i.e. there will be a + // minimum of 28 guard digits retained. (Using + 1.5 would give [9, 21] guard digits.) + k = POW_PRECISION ? mathceil( POW_PRECISION / LOG_BASE + 2 ) : 0; + y = new BigNumber(ONE); + + for ( ; ; ) { + + if ( i % 2 ) { + y = y.times(x); + if ( !y.c ) break; + if ( k && y.c.length > k ) y.c.length = k; + } + + i = mathfloor( i / 2 ); + if ( !i ) break; + + x = x.times(x); + if ( k && x.c && x.c.length > k ) x.c.length = k; + } + + if ( n < 0 ) y = ONE.div(y); + return k ? round( y, POW_PRECISION, ROUNDING_MODE ) : y; + }; + + + /* + * Return a string representing the value of this BigNumber rounded to sd significant digits + * using rounding mode rm or ROUNDING_MODE. If sd is less than the number of digits + * necessary to represent the integer part of the value in fixed-point notation, then use + * exponential notation. + * + * [sd] {number} Significant digits. Integer, 1 to MAX inclusive. + * [rm] {number} Rounding mode. Integer, 0 to 8 inclusive. + * + * 'toPrecision() precision not an integer: {sd}' + * 'toPrecision() precision out of range: {sd}' + * 'toPrecision() rounding mode not an integer: {rm}' + * 'toPrecision() rounding mode out of range: {rm}' + */ + P.toPrecision = function ( sd, rm ) { + return format( this, sd != null && isValidInt( sd, 1, MAX, 24, 'precision' ) + ? sd | 0 : null, rm, 24 ); + }; + + + /* + * Return a string representing the value of this BigNumber in base b, or base 10 if b is + * omitted. If a base is specified, including base 10, round according to DECIMAL_PLACES and + * ROUNDING_MODE. If a base is not specified, and this BigNumber has a positive exponent + * that is equal to or greater than TO_EXP_POS, or a negative exponent equal to or less than + * TO_EXP_NEG, return exponential notation. + * + * [b] {number} Integer, 2 to 64 inclusive. + * + * 'toString() base not an integer: {b}' + * 'toString() base out of range: {b}' + */ + P.toString = function (b) { + var str, + n = this, + s = n.s, + e = n.e; + + // Infinity or NaN? + if ( e === null ) { + + if (s) { + str = 'Infinity'; + if ( s < 0 ) str = '-' + str; + } else { + str = 'NaN'; + } + } else { + str = coeffToString( n.c ); + + if ( b == null || !isValidInt( b, 2, 64, 25, 'base' ) ) { + str = e <= TO_EXP_NEG || e >= TO_EXP_POS + ? toExponential( str, e ) + : toFixedPoint( str, e ); + } else { + str = convertBase( toFixedPoint( str, e ), b | 0, 10, s ); + } + + if ( s < 0 && n.c[0] ) str = '-' + str; + } + + return str; + }; + + + /* + * Return a new BigNumber whose value is the value of this BigNumber truncated to a whole + * number. + */ + P.truncated = P.trunc = function () { + return round( new BigNumber(this), this.e + 1, 1 ); + }; + + + + /* + * Return as toString, but do not accept a base argument. + */ + P.valueOf = P.toJSON = function () { + return this.toString(); + }; + + + // Aliases for BigDecimal methods. + //P.add = P.plus; // P.add included above + //P.subtract = P.minus; // P.sub included above + //P.multiply = P.times; // P.mul included above + //P.divide = P.div; + //P.remainder = P.mod; + //P.compareTo = P.cmp; + //P.negate = P.neg; + + + if ( configObj != null ) BigNumber.config(configObj); + + return BigNumber; + } + + + // PRIVATE HELPER FUNCTIONS + + + function bitFloor(n) { + var i = n | 0; + return n > 0 || n === i ? i : i - 1; + } + + + // Return a coefficient array as a string of base 10 digits. + function coeffToString(a) { + var s, z, + i = 1, + j = a.length, + r = a[0] + ''; + + for ( ; i < j; ) { + s = a[i++] + ''; + z = LOG_BASE - s.length; + for ( ; z--; s = '0' + s ); + r += s; + } + + // Determine trailing zeros. + for ( j = r.length; r.charCodeAt(--j) === 48; ); + return r.slice( 0, j + 1 || 1 ); + } + + + // Compare the value of BigNumbers x and y. + function compare( x, y ) { + var a, b, + xc = x.c, + yc = y.c, + i = x.s, + j = y.s, + k = x.e, + l = y.e; + + // Either NaN? + if ( !i || !j ) return null; + + a = xc && !xc[0]; + b = yc && !yc[0]; + + // Either zero? + if ( a || b ) return a ? b ? 0 : -j : i; + + // Signs differ? + if ( i != j ) return i; + + a = i < 0; + b = k == l; + + // Either Infinity? + if ( !xc || !yc ) return b ? 0 : !xc ^ a ? 1 : -1; + + // Compare exponents. + if ( !b ) return k > l ^ a ? 1 : -1; + + j = ( k = xc.length ) < ( l = yc.length ) ? k : l; + + // Compare digit by digit. + for ( i = 0; i < j; i++ ) if ( xc[i] != yc[i] ) return xc[i] > yc[i] ^ a ? 1 : -1; + + // Compare lengths. + return k == l ? 0 : k > l ^ a ? 1 : -1; + } + + + /* + * Return true if n is a valid number in range, otherwise false. + * Use for argument validation when ERRORS is false. + * Note: parseInt('1e+1') == 1 but parseFloat('1e+1') == 10. + */ + function intValidatorNoErrors( n, min, max ) { + return ( n = truncate(n) ) >= min && n <= max; + } + + + function isArray(obj) { + return Object.prototype.toString.call(obj) == '[object Array]'; + } + + + /* + * Convert string of baseIn to an array of numbers of baseOut. + * Eg. convertBase('255', 10, 16) returns [15, 15]. + * Eg. convertBase('ff', 16, 10) returns [2, 5, 5]. + */ + function toBaseOut( str, baseIn, baseOut ) { + var j, + arr = [0], + arrL, + i = 0, + len = str.length; + + for ( ; i < len; ) { + for ( arrL = arr.length; arrL--; arr[arrL] *= baseIn ); + arr[ j = 0 ] += ALPHABET.indexOf( str.charAt( i++ ) ); + + for ( ; j < arr.length; j++ ) { + + if ( arr[j] > baseOut - 1 ) { + if ( arr[j + 1] == null ) arr[j + 1] = 0; + arr[j + 1] += arr[j] / baseOut | 0; + arr[j] %= baseOut; + } + } + } + + return arr.reverse(); + } + + + function toExponential( str, e ) { + return ( str.length > 1 ? str.charAt(0) + '.' + str.slice(1) : str ) + + ( e < 0 ? 'e' : 'e+' ) + e; + } + + + function toFixedPoint( str, e ) { + var len, z; + + // Negative exponent? + if ( e < 0 ) { + + // Prepend zeros. + for ( z = '0.'; ++e; z += '0' ); + str = z + str; + + // Positive exponent + } else { + len = str.length; + + // Append zeros. + if ( ++e > len ) { + for ( z = '0', e -= len; --e; z += '0' ); + str += z; + } else if ( e < len ) { + str = str.slice( 0, e ) + '.' + str.slice(e); + } + } + + return str; + } + + + function truncate(n) { + n = parseFloat(n); + return n < 0 ? mathceil(n) : mathfloor(n); + } + + + // EXPORT + + + BigNumber = another(); + + // AMD. + if ( typeof define == 'function' && define.amd ) { + define( function () { return BigNumber; } ); + + // Node and other environments that support module.exports. + } else if ( typeof module != 'undefined' && module.exports ) { + module.exports = BigNumber; + if ( !crypto ) try { crypto = require('crypto'); } catch (e) {} + + // Browser. + } else { + global.BigNumber = BigNumber; + } +})(this); + +},{"crypto":1}],"natspec":[function(require,module,exports){ +/* + This file is part of natspec.js. + + natspec.js is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + natspec.js is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with natspec.js. If not, see . +*/ +/** @file natspec.js + * @authors: + * Marek Kotewicz + * @date 2015 + */ + +var abi = require('./node_modules/ethereum.js/lib/abi.js'); + +/** + * This object should be used to evaluate natspec expression + * It has one method evaluateExpression which shoul be used + */ +var natspec = (function () { + /** + * Helper method + * Should be called to copy values from object to global context + * + * @method copyToContext + * @param {Object} object from which we want to copy properties + * @param {Object} object to which we copy + */ + var copyToContext = function (obj, context) { + Object.keys(obj).forEach(function (key) { + context[key] = obj[key]; + }); + } + + /** + * Should be used to generate codes, which will be evaluated + * + * @method generateCode + * @param {Object} object from which code will be generated + * @return {String} javascript code which is used to initalized variables + */ + var generateCode = function (obj) { + return Object.keys(obj).reduce(function (acc, key) { + return acc + "var " + key + " = context['" + key + "'];\n"; + }, ""); + }; + + /** + * Helper method + * Should be called to get method with given name from the abi + * + * @method getMethodWithName + * @param {Array} contract's abi + * @param {String} name of the method that we are looking for + * @return {Object} abi for method with name + */ + var getMethodWithName = function(abi, name) { + return abi.filter(function (method) { + return method.name === name; + })[0]; + }; + + /** + * Should be used to get all contract method input variables + * + * @method getMethodInputParams + * @param {Object} abi for certain method + * @param {Object} transaction object + * @return {Object} object with all contract's method input variables + */ + var getMethodInputParams = function (method, transaction) { + // do it with output formatter (cause we have to decode) + var params = abi.formatOutput(method.inputs, '0x' + transaction.params[0].data.slice(10)); + + return method.inputs.reduce(function (acc, current, index) { + acc[current.name] = params[index]; + return acc; + }, {}); + }; + + /** + * Should be called when we want to evaluate natspec expression + * Replaces all natspec 'subexpressions' with evaluated value + * + * @method mapExpressionToEvaluate + * @param {String} expression to evaluate + * @param {Function} callback which is called to evaluate te expression + * @return {String} evaluated expression + */ + var mapExpressionsToEvaluate = function (expression, cb) { + var evaluatedExpression = ""; + + // match everything in `` quotes + var pattern = /\`(?:\\.|[^`\\])*\`/gim + var match; + var lastIndex = 0; + try { + while ((match = pattern.exec(expression)) !== null) { + var startIndex = pattern.lastIndex - match[0].length; + var toEval = match[0].slice(1, match[0].length - 1); + evaluatedExpression += expression.slice(lastIndex, startIndex); + var evaluatedPart = cb(toEval); + evaluatedExpression += evaluatedPart; + lastIndex = pattern.lastIndex; + } + + evaluatedExpression += expression.slice(lastIndex); + } + catch (err) { + throw new Error("Natspec evaluation failed, wrong input params"); + } + + return evaluatedExpression; + }; + + /** + * Should be called to evaluate single expression + * Is internally using javascript's 'eval' method + * + * @method evaluateExpression + * @param {String} expression which should be evaluated + * @param {Object} [call] object containing contract abi, transaction, called method + * @return {String} evaluated expression + * @throws exception if method is not found or we are trying to evaluate input params that does not exists + */ + var evaluateExpression = function (expression, call) { + //var self = this; + var context = {}; + + if (!!call) { + try { + var method = getMethodWithName(call.abi, call.method); + var params = getMethodInputParams(method, call.transaction); + copyToContext(params, context); + } + catch (err) { + throw new Error("Natspec evaluation failed, method does not exist"); + } + } + + var code = generateCode(context); + + var evaluatedExpression = mapExpressionsToEvaluate(expression, function (toEval) { + var fn = new Function("context", code + "return " + toEval + ";"); + return fn(context).toString(); + }); + + return evaluatedExpression; + }; + + /** + * Safe version of evaluateExpression + * Instead of throwing an exception it returns it as a string + * + * @method evaluateExpressionSafe + * @param {String} expression which should be evaluated + * @param {Object} [call] object containing contract abi, transaction, called method + * @return {String} evaluated expression + */ + var evaluateExpressionSafe = function (expression, call) { + try { + return evaluateExpression(expression, call); + } + catch (err) { + return err.message; + } + }; + + return { + evaluateExpression: evaluateExpression, + evaluateExpressionSafe: evaluateExpressionSafe + }; + +})(); + +module.exports = natspec; + + +},{"./node_modules/ethereum.js/lib/abi.js":3}]},{},[]); diff --git a/dist/natspec.js.map b/dist/natspec.js.map new file mode 100644 index 000000000..126dc498d --- /dev/null +++ b/dist/natspec.js.map @@ -0,0 +1 @@ +{"version":3,"file":"natspec.min.js","sources":["natspec.js"],"names":["require","e","t","n","r","s","o","u","a","i","f","Error","code","l","exports","call","length",1,2,"module","drainQueue","draining","currentQueue","len","queue","noop","process","nextTick","fun","push","setTimeout","title","browser","env","argv","version","on","addListener","once","off","removeListener","removeAllListeners","emit","binding","cwd","chdir","umask",3,"utils","types","c","displayTypeError","type","console","error","arrayType","slice","dynamicTypeBytes","value","formatInputInt","inputTypes","formatInput","inputs","params","bytes","toAppendConstant","toAppendArrayContent","forEach","input","index","typeMatch","j","formatter","format","reduce","acc","curr","dynamicBytesLength","ETH_PADDING","outputTypes","formatOutput","outs","output","result","padding","dynamicPartLength","dynamicPart","out","size","formatOutputUInt","array","k","prefixedType","inputParser","json","parser","method","displayName","extractDisplayName","name","typeName","extractTypeName","impl","Array","prototype","arguments","undefined","outputParser","outputs","./const","./formatters","./types","./utils",4,"NODE_ENV","BigNumber","ETH_UNITS","ETH_SIGNATURE_LENGTH","ETH_BIGNUMBER_ROUNDING_MODE","ROUNDING_MODE","ROUND_DOWN","ETH_POLLING_TIMEOUT","this","_process","bignumber.js",5,"padLeft","string","chars","sign","join","config","round","lessThan","plus","toString","indexOf","substr","formatInputString","fromAscii","formatInputBool","formatInputReal","times","pow","signedIsNegative","formatOutputInt","minus","formatOutputReal","dividedBy","formatOutputUReal","formatOutputHash","formatOutputBool","formatOutputString","toAscii","formatOutputAddress",6,"prefix","namedType",7,"findIndex","callback","end","hex","str","substring","parseInt","String","fromCharCode","toHex","charCodeAt","pad","replace","filterFunctions","filter","current","filterEvents","toEth","val","unit","units","toFixed","replaceFunction","$0","$1","$2",8,"global","another","configObj","b","num","x","ERRORS","raise","isValidInt","id","DECIMAL_PLACES","RegExp","ALPHABET","test","parseNumeric","tooManyDigits","convertBase","isNumeric","search","MAX_EXP","MIN_EXP","LOG_BASE","baseOut","baseIn","d","xc","y","dp","rm","toLowerCase","POW_PRECISION","toBaseOut","toFixedPoint","coeffToString","pop","div","unshift","charAt","caller","c0","ne","roundingMode","TO_EXP_NEG","toExponential","maxOrMin","args","m","isArray","intValidatorWithErrors","min","max","truncate","normalise","msg","sd","ni","rd","pows10","POWS_TEN","mathceil","mathfloor","BASE","P","ONE","TO_EXP_POS","CRYPTO","MODULO_MODE","FORMAT","decimalSeparator","groupSeparator","groupSize","secondaryGroupSize","fractionGroupSeparator","fractionGroupSize","ROUND_UP","ROUND_CEIL","ROUND_FLOOR","ROUND_HALF_UP","ROUND_HALF_DOWN","ROUND_HALF_EVEN","ROUND_HALF_CEIL","ROUND_HALF_FLOOR","EUCLID","v","p","has","hasOwnProperty","MAX","intValidatorNoErrors","notBool","crypto","lt","gt","random","pow2_53","random53bitInt","Math","rand","getRandomValues","Uint32Array","randomBytes","copy","shift","multiply","base","temp","xlo","xhi","carry","klo","SQRT_BASE","khi","compare","aL","bL","cmp","subtract","more","prod","prodL","q","qc","rem","remL","rem0","xi","xL","yc0","yL","yz","yc","NaN","bitFloor","basePrefix","dotAfter","dotBefore","isInfinityOrNaN","whitespaceOrPlus","isNaN","p1","p2","absoluteValue","abs","ceil","comparedTo","decimalPlaces","dividedToIntegerBy","divToInt","equals","eq","floor","greaterThan","greaterThanOrEqualTo","gte","isFinite","isInteger","isInt","isNegative","isNeg","isZero","lessThanOrEqualTo","lte","sub","xLTy","xe","ye","reverse","modulo","mod","negated","neg","add","precision","z","MAX_SAFE_INTEGER","squareRoot","sqrt","rep","half","mul","xcL","ycL","ylo","yhi","zc","sqrtBase","toDigits","toFormat","arr","split","g1","g2","intPart","fractionPart","intDigits","toFraction","md","d0","d2","exp","n0","n1","d1","toNumber","toPower","parseFloat","toPrecision","truncated","trunc","valueOf","toJSON","obj","Object","arrL","define","amd","natspec","abi","copyToContext","context","keys","key","generateCode","getMethodWithName","getMethodInputParams","transaction","data","mapExpressionsToEvaluate","expression","cb","match","evaluatedExpression","pattern","lastIndex","exec","startIndex","toEval","evaluatedPart","err","evaluateExpression","fn","Function","evaluateExpressionSafe","message","./node_modules/ethereum.js/lib/abi.js"],"mappings":"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"} \ No newline at end of file diff --git a/dist/natspec.min.js b/dist/natspec.min.js new file mode 100644 index 000000000..6cf28d756 --- /dev/null +++ b/dist/natspec.min.js @@ -0,0 +1,2 @@ +require=function t(e,n,r){function i(f,u){if(!n[f]){if(!e[f]){var s="function"==typeof require&&require;if(!u&&s)return s(f,!0);if(o)return o(f,!0);var c=new Error("Cannot find module '"+f+"'");throw c.code="MODULE_NOT_FOUND",c}var a=n[f]={exports:{}};e[f][0].call(a.exports,function(t){var n=e[f][1][t];return i(n?n:t)},a,a.exports,t,e,n,r)}return n[f].exports}for(var o="function"==typeof require&&require,f=0;fv;v++)d.push(g(e.slice(0,s))),e=e.slice(s);n.push(d)}else r.prefixedType("string")(t[c].type)?(a=a.slice(s),n.push(g(e.slice(0,s))),e=e.slice(s)):(n.push(g(e.slice(0,s))),e=e.slice(s))}),n},g=function(t){var e={};return t.forEach(function(t){var r=n.extractDisplayName(t.name),i=n.extractTypeName(t.name),o=function(){var e=Array.prototype.slice.call(arguments);return a(t.inputs,e)};void 0===e[r]&&(e[r]=o),e[r][i]=o}),e},m=function(t){var e={};return t.forEach(function(t){var r=n.extractDisplayName(t.name),i=n.extractTypeName(t.name),o=function(e){return h(t.outputs,e)};void 0===e[r]&&(e[r]=o),e[r][i]=o}),e};e.exports={inputParser:g,outputParser:m,formatInput:a,formatOutput:h}},{"./const":4,"./formatters":5,"./types":6,"./utils":7}],4:[function(t,e){(function(n){if("build"!==n.env.NODE_ENV)var r=t("bignumber.js");var i=["wei","Kwei","Mwei","Gwei","szabo","finney","ether","grand","Mether","Gether","Tether","Pether","Eether","Zether","Yether","Nether","Dether","Vether","Uether"];e.exports={ETH_PADDING:32,ETH_SIGNATURE_LENGTH:4,ETH_UNITS:i,ETH_BIGNUMBER_ROUNDING_MODE:{ROUNDING_MODE:r.ROUND_DOWN},ETH_POLLING_TIMEOUT:1e3}}).call(this,t("_process"))},{_process:2,"bignumber.js":8}],5:[function(t,e){(function(n){if("build"!==n.env.NODE_ENV)var r=t("bignumber.js");var i=t("./utils"),o=t("./const"),f=function(t,e,n){return new Array(e-t.length+1).join(n?n:"0")+t},u=function(t){var e=2*o.ETH_PADDING;return t instanceof r||"number"==typeof t?("number"==typeof t&&(t=new r(t)),r.config(o.ETH_BIGNUMBER_ROUNDING_MODE),t=t.round(),t.lessThan(0)&&(t=new r("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",16).plus(t).plus(1)),t=t.toString(16)):t=0===t.indexOf("0x")?t.substr(2):"string"==typeof t?u(new r(t)):(+t).toString(16),f(t,e)},s=function(t){return i.fromAscii(t,o.ETH_PADDING).substr(2)},c=function(t){return"000000000000000000000000000000000000000000000000000000000000000"+(t?"1":"0")},a=function(t){return u(new r(t).times(new r(2).pow(128)))},l=function(t){return"1"===new r(t.substr(0,1),16).toString(2).substr(0,1)},p=function(t){return t=t||"0",l(t)?new r(t,16).minus(new r("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",16)).minus(1):new r(t,16)},h=function(t){return t=t||"0",new r(t,16)},g=function(t){return p(t).dividedBy(new r(2).pow(128))},m=function(t){return h(t).dividedBy(new r(2).pow(128))},d=function(t){return"0x"+t},v=function(t){return"0000000000000000000000000000000000000000000000000000000000000001"===t?!0:!1},w=function(t){return i.toAscii(t)},y=function(t){return"0x"+t.slice(t.length-40,t.length)};e.exports={formatInputInt:u,formatInputString:s,formatInputBool:c,formatInputReal:a,formatOutputInt:p,formatOutputUInt:h,formatOutputReal:g,formatOutputUReal:m,formatOutputHash:d,formatOutputBool:v,formatOutputString:w,formatOutputAddress:y}}).call(this,t("_process"))},{"./const":4,"./utils":7,_process:2,"bignumber.js":8}],6:[function(t,e){var n=t("./formatters"),r=function(t){return function(e){return 0===e.indexOf(t)}},i=function(t){return function(e){return t===e}},o=function(){return[{type:r("uint"),format:n.formatInputInt},{type:r("int"),format:n.formatInputInt},{type:r("hash"),format:n.formatInputInt},{type:r("string"),format:n.formatInputString},{type:r("real"),format:n.formatInputReal},{type:r("ureal"),format:n.formatInputReal},{type:i("address"),format:n.formatInputInt},{type:i("bool"),format:n.formatInputBool}]},f=function(){return[{type:r("uint"),format:n.formatOutputUInt},{type:r("int"),format:n.formatOutputInt},{type:r("hash"),format:n.formatOutputHash},{type:r("string"),format:n.formatOutputString},{type:r("real"),format:n.formatOutputReal},{type:r("ureal"),format:n.formatOutputUReal},{type:i("address"),format:n.formatOutputAddress},{type:i("bool"),format:n.formatOutputBool}]};e.exports={prefixedType:r,namedType:i,inputTypes:o,outputTypes:f}},{"./formatters":5}],7:[function(t,e){var n=t("./const"),r=function(t,e){for(var n=!1,r=0;rn;n+=2){var i=parseInt(t.substr(n,2),16);if(0===i)break;e+=String.fromCharCode(i)}return e},o=function(t){for(var e="",n=0;n3e3&&rr?"i":"").test(c))return m(a,c,u,r);u?(a.s=0>1/t?(c=c.slice(1),-1):1,$&&c.replace(/^0\.0*|\./,"").length>15&&U(L,O,t),u=!1):a.s=45===c.charCodeAt(0)?(c=c.slice(1),-1):1,c=n(c,10,r,a.s)}else{if(t instanceof e)return a.s=t.s,a.e=t.e,a.c=(t=t.c)?t.slice():t,void(L=0);if((u="number"==typeof t)&&0*t==0){if(a.s=0>1/t?(t=-t,-1):1,t===~~t){for(o=0,f=t;f>=10;f/=10,o++);return a.e=o,a.c=[t],void(L=0)}c=t+""}else{if(!d.test(c=t+""))return m(a,c,u);a.s=45===c.charCodeAt(0)?(c=c.slice(1),-1):1}}for((o=c.indexOf("."))>-1&&(c=c.replace(".","")),(f=c.search(/e/i))>0?(0>o&&(o=f),o+=+c.slice(f+1),c=c.substring(0,f)):0>o&&(o=c.length),f=0;48===c.charCodeAt(f);f++);for(s=c.length;48===c.charCodeAt(--s););if(c=c.slice(f,s+1))if(s=c.length,u&&$&&s>15&&U(L,O,a.s*t),o=o-f-1,o>q)a.c=a.e=null;else if(k>o)a.c=[a.e=0];else{if(a.e=o,a.c=[],f=(o+1)%I,0>o&&(f+=I),s>f){for(f&&a.c.push(+c.slice(0,f)),s-=I;s>f;)a.c.push(+c.slice(f,f+=I));c=c.slice(f),f=I-c.length}else f-=s;for(;f--;c+="0");a.c.push(+c)}else a.c=[a.e=0];L=0}function n(t,n,r,i){var f,u,s,a,p,h,g,m=t.indexOf("."),d=B,v=H;for(37>r&&(t=t.toLowerCase()),m>=0&&(s=Y,Y=0,t=t.replace(".",""),g=new e(r),p=g.pow(t.length-m),Y=s,g.c=c(l(o(p.c),p.e),10,n),g.e=g.c.length),h=c(t,r,n),u=s=h.length;0==h[--s];h.pop());if(!h[0])return"0";if(0>m?--u:(p.c=h,p.e=u,p.s=i,p=G(p,g,d,v,n),h=p.c,a=p.r,u=p.e),f=u+d+1,m=h[f],s=n/2,a=a||0>f||null!=h[f+1],a=4>v?(null!=m||a)&&(0==v||v==(p.s<0?3:2)):m>s||m==s&&(4==v||a||6==v&&1&h[f-1]||v==(p.s<0?8:7)),1>f||!h[0])t=a?l("1",-d):"0";else{if(h.length=f,a)for(--n;++h[--f]>n;)h[f]=0,f||(++u,h.unshift(1));for(s=h.length;!h[--s];);for(m=0,t="";s>=m;t+=N.charAt(h[m++]));t=l(t,u)}return t}function h(t,n,r,i){var f,u,s,c,p;if(r=null!=r&&z(r,0,8,i,b)?0|r:H,!t.c)return t.toString();if(f=t.c[0],s=t.e,null==n)p=o(t.c),p=19==i||24==i&&C>=s?a(p,s):l(p,s);else if(t=F(new e(t),n,r),u=t.e,p=o(t.c),c=p.length,19==i||24==i&&(u>=n||C>=u)){for(;n>c;p+="0",c++);p=a(p,u)}else if(n-=s,p=l(p,u),u+1>c){if(--n>0)for(p+=".";n--;p+="0");}else if(n+=u-c,n>0)for(u+1==c&&(p+=".");n--;p+="0");return t.s<0&&f?"-"+p:p}function S(t,n){var r,i,o=0;for(s(t[0])&&(t=t[0]),r=new e(t[0]);++ot||t>n||t!=p(t))&&U(r,(i||"decimal places")+(e>t||t>n?" out of range":" not an integer"),t),!0}function R(t,e,n){for(var r=1,i=e.length;!e[--i];e.pop());for(i=e[0];i>=10;i/=10,r++);return(n=r+n*I-1)>q?t.c=t.e=null:k>n?t.c=[t.e=0]:(t.e=n,t.c=e),t}function U(t,e,n){var r=new Error(["new BigNumber","cmp","config","div","divToInt","eq","gt","gte","lt","lte","minus","mod","plus","precision","random","round","shift","times","toDigits","toExponential","toFixed","toFormat","toFraction","pow","toPrecision","toString","BigNumber"][t]+"() "+e+": "+n);throw r.name="BigNumber Error",L=0,r}function F(t,e,n,r){var i,o,f,u,s,c,a,l=t.c,p=_;if(l){t:{for(i=1,u=l[0];u>=10;u/=10,i++);if(o=e-i,0>o)o+=I,f=e,s=l[c=0],a=s/p[i-f-1]%10|0;else if(c=v((o+1)/I),c>=l.length){if(!r)break t;for(;l.length<=c;l.push(0));s=a=0,i=1,o%=I,f=o-I+1}else{for(s=u=l[c],i=1;u>=10;u/=10,i++);o%=I,f=o-I+i,a=0>f?0:s/p[i-f-1]%10|0}if(r=r||0>e||null!=l[c+1]||(0>f?s:s%p[i-f-1]),r=4>n?(a||r)&&(0==n||n==(t.s<0?3:2)):a>5||5==a&&(4==n||r||6==n&&(o>0?f>0?s/p[i-f]:0:l[c-1])%10&1||n==(t.s<0?8:7)),1>e||!l[0])return l.length=0,r?(e-=t.e+1,l[0]=p[e%I],t.e=-e||0):l[0]=t.e=0,t;if(0==o?(l.length=c,u=1,c--):(l.length=c+1,u=p[I-o],l[c]=f>0?w(s/p[i-f]%p[f])*u:0),r)for(;;){if(0==c){for(o=1,f=l[0];f>=10;f/=10,o++);for(f=l[0]+=u,u=1;f>=10;f/=10,u++);o!=u&&(t.e++,l[0]==E&&(l[0]=1));break}if(l[c]+=u,l[c]!=E)break;l[c--]=0,u=1}for(o=l.length;0===l[--o];l.pop());}t.e>q?t.c=t.e=null:t.en?null!=(t=i[n++]):void 0};return f(e="DECIMAL_PLACES")&&z(t,0,D,2,e)&&(B=0|t),r[e]=B,f(e="ROUNDING_MODE")&&z(t,0,8,2,e)&&(H=0|t),r[e]=H,f(e="EXPONENTIAL_AT")&&(s(t)?z(t[0],-D,0,2,e)&&z(t[1],0,D,2,e)&&(C=0|t[0],j=0|t[1]):z(t,-D,D,2,e)&&(C=-(j=0|(0>t?-t:t)))),r[e]=[C,j],f(e="RANGE")&&(s(t)?z(t[0],-D,-1,2,e)&&z(t[1],1,D,2,e)&&(k=0|t[0],q=0|t[1]):z(t,-D,D,2,e)&&(0|t?k=-(q=0|(0>t?-t:t)):$&&U(2,e+" cannot be zero",t))),r[e]=[k,q],f(e="ERRORS")&&(t===!!t||1===t||0===t?(L=0,z=($=!!t)?A:u):$&&U(2,e+y,t)),r[e]=$,f(e="CRYPTO")&&(t===!!t||1===t||0===t?(V=!(!t||!g||"object"!=typeof g),t&&!V&&$&&U(2,"crypto unavailable",g)):$&&U(2,e+y,t)),r[e]=V,f(e="MODULO_MODE")&&z(t,0,9,2,e)&&(W=0|t),r[e]=W,f(e="POW_PRECISION")&&z(t,0,D,2,e)&&(Y=0|t),r[e]=Y,f(e="FORMAT")&&("object"==typeof t?Z=t:$&&U(2,e+" not an object",t)),r[e]=Z,r},e.max=function(){return S(arguments,M.lt)},e.min=function(){return S(arguments,M.gt)},e.random=function(){var t=9007199254740992,n=Math.random()*t&2097151?function(){return w(Math.random()*t)}:function(){return 8388608*(1073741824*Math.random()|0)+(8388608*Math.random()|0)};return function(t){var r,i,o,f,u,s=0,c=[],a=new e(P);if(t=null!=t&&z(t,0,D,14)?0|t:B,f=v(t/I),V)if(g&&g.getRandomValues){for(r=g.getRandomValues(new Uint32Array(f*=2));f>s;)u=131072*r[s]+(r[s+1]>>>11),u>=9e15?(i=g.getRandomValues(new Uint32Array(2)),r[s]=i[0],r[s+1]=i[1]):(c.push(u%1e14),s+=2);s=f/2}else if(g&&g.randomBytes){for(r=g.randomBytes(f*=7);f>s;)u=281474976710656*(31&r[s])+1099511627776*r[s+1]+4294967296*r[s+2]+16777216*r[s+3]+(r[s+4]<<16)+(r[s+5]<<8)+r[s+6],u>=9e15?g.randomBytes(7).copy(r,s):(c.push(u%1e14),s+=7);s=f/7}else $&&U(14,"crypto unavailable",g);if(!s)for(;f>s;)u=n(),9e15>u&&(c[s++]=u%1e14);for(f=c[--s],t%=I,f&&t&&(u=_[I-t],c[s]=w(f/u)*u);0===c[s];c.pop(),s--);if(0>s)c=[o=0];else{for(o=-1;0===c[0];c.shift(),o-=I);for(s=1,u=c[0];u>=10;u/=10,s++);I>s&&(o-=I-s)}return a.e=o,a.c=c,a}}(),G=function(){function t(t,e,n){var r,i,o,f,u=0,s=t.length,c=e%T,a=e/T|0;for(t=t.slice();s--;)o=t[s]%T,f=t[s]/T|0,r=a*o+f*c,i=c*o+r%T*T+u,u=(i/n|0)+(r/T|0)+a*f,t[s]=i%n;return u&&t.unshift(u),t}function n(t,e,n,r){var i,o;if(n!=r)o=n>r?1:-1;else for(i=o=0;n>i;i++)if(t[i]!=e[i]){o=t[i]>e[i]?1:-1;break}return o}function r(t,e,n,r){for(var i=0;n--;)t[n]-=i,i=t[n]1;t.shift());}return function(o,f,u,s,c){var a,l,p,h,g,m,d,v,y,b,O,N,x,_,T,D,S,A=o.s==f.s?1:-1,R=o.c,U=f.c;if(!(R&&R[0]&&U&&U[0]))return new e(o.s&&f.s&&(R?!U||R[0]!=U[0]:U)?R&&0==R[0]||!U?0*A:A/0:0/0);for(v=new e(A),y=v.c=[],l=o.e-f.e,A=u+l+1,c||(c=E,l=i(o.e/I)-i(f.e/I),A=A/I|0),p=0;U[p]==(R[p]||0);p++);if(U[p]>(R[p]||0)&&l--,0>A)y.push(1),h=!0;else{for(_=R.length,D=U.length,p=0,A+=2,g=w(c/(U[0]+1)),g>1&&(U=t(U,g,c),R=t(R,g,c),D=U.length,_=R.length),x=D,b=R.slice(0,D),O=b.length;D>O;b[O++]=0);S=U.slice(),S.unshift(0),T=U[0],U[1]>=c/2&&T++;do g=0,a=n(U,b,D,O),0>a?(N=b[0],D!=O&&(N=N*c+(b[1]||0)),g=w(N/T),g>1?(g>=c&&(g=c-1),m=t(U,g,c),d=m.length,O=b.length,a=n(m,b,d,O),1==a&&(g--,r(m,d>D?S:U,d,c))):(0==g&&(a=g=1),m=U.slice()),d=m.length,O>d&&m.unshift(0),r(b,m,O,c),-1==a&&(O=b.length,a=n(U,b,D,O),1>a&&(g++,r(b,O>D?S:U,O,c))),O=b.length):0===a&&(g++,b=[0]),y[p++]=g,a&&b[0]?b[O++]=R[x]||0:(b=[R[x]],O=1);while((x++<_||null!=b[0])&&A--);h=null!=b[0],y[0]||y.shift()}if(c==E){for(p=1,A=y[0];A>=10;A/=10,p++);F(v,u+(v.e=p+l*I-1)+1,s,h)}else v.e=l,v.r=+h;return v}}(),m=function(){var t=/^(-?)0([xbo])(?=\w[\w.]*$)/i,n=/^([^.]+)\.$/,r=/^\.([^.]+)$/,i=/^-?(Infinity|NaN)$/,o=/^\s*\+(?=[\w.])|^\s+|\s+$/g;return function(f,u,s,c){var a,l=s?u:u.replace(o,"");if(i.test(l))f.s=isNaN(l)?null:0>l?-1:1;else{if(!s&&(l=l.replace(t,function(t,e,n){return a="x"==(n=n.toLowerCase())?16:"b"==n?2:8,c&&c!=a?t:e}),c&&(a=c,l=l.replace(n,"$1").replace(r,"0.$1")),u!=l))return new e(l,a);$&&U(L,"not a"+(c?" base "+c:"")+" number",u),f.s=null}f.c=f.e=null,L=0}}(),M.absoluteValue=M.abs=function(){var t=new e(this);return t.s<0&&(t.s=1),t},M.ceil=function(){return F(new e(this),this.e+1,2)},M.comparedTo=M.cmp=function(t,n){return L=1,f(this,new e(t,n))},M.decimalPlaces=M.dp=function(){var t,e,n=this.c;if(!n)return null;if(t=((e=n.length-1)-i(this.e/I))*I,e=n[e])for(;e%10==0;e/=10,t--);return 0>t&&(t=0),t},M.dividedBy=M.div=function(t,n){return L=3,G(this,new e(t,n),B,H)},M.dividedToIntegerBy=M.divToInt=function(t,n){return L=4,G(this,new e(t,n),0,1)},M.equals=M.eq=function(t,n){return L=5,0===f(this,new e(t,n))},M.floor=function(){return F(new e(this),this.e+1,3)},M.greaterThan=M.gt=function(t,n){return L=6,f(this,new e(t,n))>0},M.greaterThanOrEqualTo=M.gte=function(t,n){return L=7,1===(n=f(this,new e(t,n)))||0===n},M.isFinite=function(){return!!this.c},M.isInteger=M.isInt=function(){return!!this.c&&i(this.e/I)>this.c.length-2},M.isNaN=function(){return!this.s},M.isNegative=M.isNeg=function(){return this.s<0},M.isZero=function(){return!!this.c&&0==this.c[0]},M.lessThan=M.lt=function(t,n){return L=8,f(this,new e(t,n))<0},M.lessThanOrEqualTo=M.lte=function(t,n){return L=9,-1===(n=f(this,new e(t,n)))||0===n},M.minus=M.sub=function(t,n){var r,o,f,u,s=this,c=s.s;if(L=10,t=new e(t,n),n=t.s,!c||!n)return new e(0/0);if(c!=n)return t.s=-n,s.plus(t);var a=s.e/I,l=t.e/I,p=s.c,h=t.c;if(!a||!l){if(!p||!h)return p?(t.s=-n,t):new e(h?s:0/0);if(!p[0]||!h[0])return h[0]?(t.s=-n,t):new e(p[0]?s:3==H?-0:0)}if(a=i(a),l=i(l),p=p.slice(),c=a-l){for((u=0>c)?(c=-c,f=p):(l=a,f=h),f.reverse(),n=c;n--;f.push(0));f.reverse()}else for(o=(u=(c=p.length)<(n=h.length))?c:n,c=n=0;o>n;n++)if(p[n]!=h[n]){u=p[n]0)for(;n--;p[r++]=0);for(n=E-1;o>c;){if(p[--o]0?(s=u,r=a):(f=-f,r=c),r.reverse();f--;r.push(0));r.reverse()}for(f=c.length,n=a.length,0>f-n&&(r=a,a=c,c=r,n=f),f=0;n;)f=(c[--n]=c[n]+a[n]+f)/E|0,c[n]%=E;return f&&(c.unshift(f),++s),R(t,c,s)},M.precision=M.sd=function(t){var e,n,r=this,i=r.c;if(null!=t&&t!==!!t&&1!==t&&0!==t&&($&&U(13,"argument"+y,t),t!=!!t&&(t=null)),!i)return null;if(n=i.length-1,e=n*I+1,n=i[n]){for(;n%10==0;n/=10,e--);for(n=i[0];n>=10;n/=10,e++);}return t&&r.e+1>e&&(e=r.e+1),e},M.round=function(t,n){var r=new e(this);return(null==t||z(t,0,D,15))&&F(r,~~t+this.e+1,null!=n&&z(n,0,8,15,b)?0|n:H),r},M.shift=function(t){var n=this;return z(t,-x,x,16,"argument")?n.times("1e"+p(t)):new e(n.c&&n.c[0]&&(-x>t||t>x)?n.s*(0>t?0:1/0):n)},M.squareRoot=M.sqrt=function(){var t,n,r,f,u,s=this,c=s.c,a=s.s,l=s.e,p=B+4,h=new e("0.5");if(1!==a||!c||!c[0])return new e(!a||0>a&&(!c||c[0])?0/0:c?s:1/0);if(a=Math.sqrt(+s),0==a||a==1/0?(n=o(c),(n.length+l)%2==0&&(n+="0"),a=Math.sqrt(n),l=i((l+1)/2)-(0>l||l%2),a==1/0?n="1e"+l:(n=a.toExponential(),n=n.slice(0,n.indexOf("e")+1)+l),r=new e(n)):r=new e(a+""),r.c[0])for(l=r.e,a=l+p,3>a&&(a=0);;)if(u=r,r=h.times(u.plus(G(s,u,p,1))),o(u.c).slice(0,a)===(n=o(r.c)).slice(0,a)){if(r.ea&&(d=b,b=O,O=d,f=a,a=h,h=f),f=a+h,d=[];f--;d.push(0));for(v=E,w=T,f=h;--f>=0;){for(r=0,g=O[f]%w,m=O[f]/w|0,s=a,u=f+s;u>f;)l=b[--s]%w,p=b[s]/w|0,c=m*l+p*g,l=g*l+c%w*w+d[u]+r,r=(l/v|0)+(c/w|0)+m*p,d[u--]=l%v;d[u]=r}return r?++o:d.shift(),R(t,d,o)},M.toDigits=function(t,n){var r=new e(this);return t=null!=t&&z(t,1,D,18,"precision")?0|t:null,n=null!=n&&z(n,0,8,18,b)?0|n:H,t?F(r,t,n):r},M.toExponential=function(t,e){return h(this,null!=t&&z(t,0,D,19)?~~t+1:null,e,19)},M.toFixed=function(t,e){return h(this,null!=t&&z(t,0,D,20)?~~t+this.e+1:null,e,20)},M.toFormat=function(t,e){var n=h(this,null!=t&&z(t,0,D,21)?~~t+this.e+1:null,e,21);if(this.c){var r,i=n.split("."),o=+Z.groupSize,f=+Z.secondaryGroupSize,u=Z.groupSeparator,s=i[0],c=i[1],a=this.s<0,l=a?s.slice(1):s,p=l.length;if(f&&(r=o,o=f,f=r,p-=r),o>0&&p>0){for(r=p%o||o,s=l.substr(0,r);p>r;r+=o)s+=u+l.substr(r,o);f>0&&(s+=u+l.slice(r)),a&&(s="-"+s)}n=c?s+Z.decimalSeparator+((f=+Z.fractionGroupSize)?c.replace(new RegExp("\\d{"+f+"}\\B","g"),"$&"+Z.fractionGroupSeparator):c):s}return n},M.toFraction=function(t){var n,r,i,f,u,s,c,a,l,p=$,h=this,g=h.c,m=new e(P),d=r=new e(P),v=c=new e(P);if(null!=t&&($=!1,s=new e(t),$=p,(!(p=s.isInt())||s.lt(P))&&($&&U(22,"max denominator "+(p?"out of range":"not an integer"),t),t=!p&&s.c&&F(s,s.e+1,1).gte(P)?s:null)),!g)return h.toString();for(l=o(g),f=m.e=l.length-h.e-1,m.c[0]=_[(u=f%I)<0?I+u:u],t=!t||s.cmp(m)>0?f>0?m:d:s,u=q,q=1/0,s=new e(l),c.c[0]=0;a=G(s,m,0,1),i=r.plus(a.times(v)),1!=i.cmp(t);)r=v,v=i,d=c.plus(a.times(i=d)),c=i,m=s.minus(a.times(i=m)),s=i;return i=G(t.minus(r),v,0,1),c=c.plus(i.times(d)),r=r.plus(i.times(v)),c.s=d.s=h.s,f*=2,n=G(d,v,f,H).minus(h).abs().cmp(G(c,r,f,H).minus(h).abs())<1?[d.toString(),v.toString()]:[c.toString(),r.toString()],q=u,n},M.toNumber=function(){var t=this;return+t||(t.s?0*t.s:0/0)},M.toPower=M.pow=function(t){var n,r,i=w(0>t?-t:+t),o=this;if(!z(t,-x,x,23,"exponent")&&(!isFinite(t)||i>x&&(t/=0)||parseFloat(t)!=t&&!(t=0/0)))return new e(Math.pow(+o,t));for(n=Y?v(Y/I+2):0,r=new e(P);;){if(i%2){if(r=r.times(o),!r.c)break;n&&r.c.length>n&&(r.c.length=n)}if(i=w(i/2),!i)break;o=o.times(o),n&&o.c&&o.c.length>n&&(o.c.length=n)}return 0>t&&(r=P.div(r)),n?F(r,Y,H):r},M.toPrecision=function(t,e){return h(this,null!=t&&z(t,1,D,24,"precision")?0|t:null,e,24)},M.toString=function(t){var e,r=this,i=r.s,f=r.e;return null===f?i?(e="Infinity",0>i&&(e="-"+e)):e="NaN":(e=o(r.c),e=null!=t&&z(t,2,64,25,"base")?n(l(e,f),0|t,10,i):C>=f||f>=j?a(e,f):l(e,f),0>i&&r.c[0]&&(e="-"+e)),e},M.truncated=M.trunc=function(){return F(new e(this),this.e+1,1)},M.valueOf=M.toJSON=function(){return this.toString()},null!=t&&e.config(t),e}function i(t){var e=0|t;return t>0||t===e?e:e-1}function o(t){for(var e,n,r=1,i=t.length,o=t[0]+"";i>r;){for(e=t[r++]+"",n=I-e.length;n--;e="0"+e);o+=e}for(i=o.length;48===o.charCodeAt(--i););return o.slice(0,i+1||1)}function f(t,e){var n,r,i=t.c,o=e.c,f=t.s,u=e.s,s=t.e,c=e.e;if(!f||!u)return null;if(n=i&&!i[0],r=o&&!o[0],n||r)return n?r?0:-u:f;if(f!=u)return f;if(n=0>f,r=s==c,!i||!o)return r?0:!i^n?1:-1;if(!r)return s>c^n?1:-1;for(u=(s=i.length)<(c=o.length)?s:c,f=0;u>f;f++)if(i[f]!=o[f])return i[f]>o[f]^n?1:-1;return s==c?0:s>c^n?1:-1}function u(t,e,n){return(t=p(t))>=e&&n>=t}function s(t){return"[object Array]"==Object.prototype.toString.call(t)}function c(t,e,n){for(var r,i,o=[0],f=0,u=t.length;u>f;){for(i=o.length;i--;o[i]*=e);for(o[r=0]+=N.indexOf(t.charAt(f++));rn-1&&(null==o[r+1]&&(o[r+1]=0),o[r+1]+=o[r]/n|0,o[r]%=n)}return o.reverse()}function a(t,e){return(t.length>1?t.charAt(0)+"."+t.slice(1):t)+(0>e?"e":"e+")+e}function l(t,e){var n,r;if(0>e){for(r="0.";++e;r+="0");t=r+t}else if(n=t.length,++e>n){for(r="0",e-=n;--e;r+="0");t+=r}else n>e&&(t=t.slice(0,e)+"."+t.slice(e));return t}function p(t){return t=parseFloat(t),0>t?v(t):w(t)}var h,g,m,d=/^-?(\d+(\.\d*)?|\.\d+)(e[+-]?\d+)?$/i,v=Math.ceil,w=Math.floor,y=" not a boolean or binary digit",b="rounding mode",O="number type has more than 15 significant digits",N="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_",E=1e14,I=14,x=9007199254740991,_=[1,10,100,1e3,1e4,1e5,1e6,1e7,1e8,1e9,1e10,1e11,1e12,1e13],T=1e7,D=1e9;if(h=r(),"function"==typeof define&&define.amd)define(function(){return h});else if("undefined"!=typeof e&&e.exports){if(e.exports=h,!g)try{g=t("crypto")}catch(S){}}else n.BigNumber=h}(this)},{crypto:1}],natspec:[function(t,e){var n=t("./node_modules/ethereum.js/lib/abi.js"),r=function(){var t=function(t,e){Object.keys(t).forEach(function(n){e[n]=t[n]})},e=function(t){return Object.keys(t).reduce(function(t,e){return t+"var "+e+" = context['"+e+"'];\n"},"")},r=function(t,e){return t.filter(function(t){return t.name===e})[0]},i=function(t,e){var r=n.formatOutput(t.inputs,"0x"+e.params[0].data.slice(10));return t.inputs.reduce(function(t,e,n){return t[e.name]=r[n],t},{})},o=function(t,e){var n,r="",i=/\`(?:\\.|[^`\\])*\`/gim,o=0;try{for(;null!==(n=i.exec(t));){var f=i.lastIndex-n[0].length,u=n[0].slice(1,n[0].length-1);r+=t.slice(o,f);var s=e(u);r+=s,o=i.lastIndex}r+=t.slice(o)}catch(c){throw new Error("Natspec evaluation failed, wrong input params")}return r},f=function(n,f){var u={};if(f)try{var s=r(f.abi,f.method),c=i(s,f.transaction);t(c,u)}catch(a){throw new Error("Natspec evaluation failed, method does not exist")}var l=e(u),p=o(n,function(t){var e=new Function("context",l+"return "+t+";");return e(u).toString()});return p},u=function(t,e){try{return f(t,e)}catch(n){return n.message}};return{evaluateExpression:f,evaluateExpressionSafe:u}}();e.exports=r},{"./node_modules/ethereum.js/lib/abi.js":3}]},{},[]); +//# sourceMappingURL=natspec.js.map \ No newline at end of file diff --git a/example/example.html b/example/example.html new file mode 100644 index 000000000..26388bee3 --- /dev/null +++ b/example/example.html @@ -0,0 +1,13 @@ + + + + + + + + + + + diff --git a/natspec.js b/natspec.js new file mode 100644 index 000000000..277cad2aa --- /dev/null +++ b/natspec.js @@ -0,0 +1,186 @@ +/* + This file is part of natspec.js. + + natspec.js is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + natspec.js is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with natspec.js. If not, see . +*/ +/** @file natspec.js + * @authors: + * Marek Kotewicz + * @date 2015 + */ + +var abi = require('./node_modules/ethereum.js/lib/abi.js'); + +/** + * This object should be used to evaluate natspec expression + * It has one method evaluateExpression which shoul be used + */ +var natspec = (function () { + /** + * Helper method + * Should be called to copy values from object to global context + * + * @method copyToContext + * @param {Object} object from which we want to copy properties + * @param {Object} object to which we copy + */ + var copyToContext = function (obj, context) { + Object.keys(obj).forEach(function (key) { + context[key] = obj[key]; + }); + } + + /** + * Should be used to generate codes, which will be evaluated + * + * @method generateCode + * @param {Object} object from which code will be generated + * @return {String} javascript code which is used to initalized variables + */ + var generateCode = function (obj) { + return Object.keys(obj).reduce(function (acc, key) { + return acc + "var " + key + " = context['" + key + "'];\n"; + }, ""); + }; + + /** + * Helper method + * Should be called to get method with given name from the abi + * + * @method getMethodWithName + * @param {Array} contract's abi + * @param {String} name of the method that we are looking for + * @return {Object} abi for method with name + */ + var getMethodWithName = function(abi, name) { + return abi.filter(function (method) { + return method.name === name; + })[0]; + }; + + /** + * Should be used to get all contract method input variables + * + * @method getMethodInputParams + * @param {Object} abi for certain method + * @param {Object} transaction object + * @return {Object} object with all contract's method input variables + */ + var getMethodInputParams = function (method, transaction) { + // do it with output formatter (cause we have to decode) + var params = abi.formatOutput(method.inputs, '0x' + transaction.params[0].data.slice(10)); + + return method.inputs.reduce(function (acc, current, index) { + acc[current.name] = params[index]; + return acc; + }, {}); + }; + + /** + * Should be called when we want to evaluate natspec expression + * Replaces all natspec 'subexpressions' with evaluated value + * + * @method mapExpressionToEvaluate + * @param {String} expression to evaluate + * @param {Function} callback which is called to evaluate te expression + * @return {String} evaluated expression + */ + var mapExpressionsToEvaluate = function (expression, cb) { + var evaluatedExpression = ""; + + // match everything in `` quotes + var pattern = /\`(?:\\.|[^`\\])*\`/gim + var match; + var lastIndex = 0; + try { + while ((match = pattern.exec(expression)) !== null) { + var startIndex = pattern.lastIndex - match[0].length; + var toEval = match[0].slice(1, match[0].length - 1); + evaluatedExpression += expression.slice(lastIndex, startIndex); + var evaluatedPart = cb(toEval); + evaluatedExpression += evaluatedPart; + lastIndex = pattern.lastIndex; + } + + evaluatedExpression += expression.slice(lastIndex); + } + catch (err) { + throw new Error("Natspec evaluation failed, wrong input params"); + } + + return evaluatedExpression; + }; + + /** + * Should be called to evaluate single expression + * Is internally using javascript's 'eval' method + * + * @method evaluateExpression + * @param {String} expression which should be evaluated + * @param {Object} [call] object containing contract abi, transaction, called method + * @return {String} evaluated expression + * @throws exception if method is not found or we are trying to evaluate input params that does not exists + */ + var evaluateExpression = function (expression, call) { + //var self = this; + var context = {}; + + if (!!call) { + try { + var method = getMethodWithName(call.abi, call.method); + var params = getMethodInputParams(method, call.transaction); + copyToContext(params, context); + } + catch (err) { + throw new Error("Natspec evaluation failed, method does not exist"); + } + } + + var code = generateCode(context); + + var evaluatedExpression = mapExpressionsToEvaluate(expression, function (toEval) { + var fn = new Function("context", code + "return " + toEval + ";"); + return fn(context).toString(); + }); + + return evaluatedExpression; + }; + + /** + * Safe version of evaluateExpression + * Instead of throwing an exception it returns it as a string + * + * @method evaluateExpressionSafe + * @param {String} expression which should be evaluated + * @param {Object} [call] object containing contract abi, transaction, called method + * @return {String} evaluated expression + */ + var evaluateExpressionSafe = function (expression, call) { + try { + return evaluateExpression(expression, call); + } + catch (err) { + return err.message; + } + }; + + return { + evaluateExpression: evaluateExpression, + evaluateExpressionSafe: evaluateExpressionSafe + }; + +})(); + +module.exports = natspec; + diff --git a/package.json b/package.json new file mode 100644 index 000000000..ed2f252db --- /dev/null +++ b/package.json @@ -0,0 +1,24 @@ +{ + "name": "natspec.js", + "version": "0.0.1", + "description": "Javascript Library used to evaluate natspec expressions", + "main": "natspec.js", + "scripts": { + "build": "cd dist && browserify -r ../natspec.js:natspec -i crypto -o natspec.js && uglifyjs natspec.js --source-map natspec.js.map -c -m -o natspec.min.js", + "test": "mocha", + "test-coveralls": "istanbul cover _mocha -- -R spec && cat coverage/lcov.info | coveralls --verbose" + }, + "author": "", + "dependencies": { + "ethereum.js": "ethereum/ethereum.js#master" + }, + "devDependencies": { + "browserify": "^9.0.3", + "chai": "^2.1.0", + "coveralls": "^2.11.2", + "istanbul": "^0.3.6", + "mocha": "^2.1.0", + "uglify-js": "^2.4.16" + }, + "license": "LGPL-3.0" +} diff --git a/test/test.js b/test/test.js new file mode 100644 index 000000000..59f46cdab --- /dev/null +++ b/test/test.js @@ -0,0 +1,149 @@ +var chai = require('chai'); +var natspec = require('../natspec.js'); +var assert = chai.assert; + +describe('natspec', function () { + it('should evaluate simple expression', function () { + // given + var expression = "`x = 1` + `y = 2` will be equal `x + y`"; + + // when + var result = natspec.evaluateExpression(expression); + var result2 = natspec.evaluateExpressionSafe(expression); + + // then + assert.equal(result, "1 + 2 will be equal 3"); + assert.equal(result2, "1 + 2 will be equal 3"); + }); + + it('should evalute expression using input params', function () { + //given + var expression = "Will multiply `a` by 7 and return `a * 7`."; + var method = 'multiply'; + var abi = [{ + "name": "multiply", + "constant": false, + "type": "function", + "inputs": [{ + "name": "a", + "type": "uint256" + }], + "outputs": [{ + "name": "d", + "type": "uint256" + }] + }]; + + var transaction = { + "jsonrpc": "2.0", + "method": "eth_call", + "params": [{ + "to": "0x8521742d3f456bd237e312d6e30724960f72517a", + "data": "0xc6888fa1000000000000000000000000000000000000000000000000000000000000007a" + }], + "id": 6 + }; + + var call = { + method: method, + abi: abi, + transaction: transaction + }; + + // when + var result = natspec.evaluateExpression(expression, call); + var result2 = natspec.evaluateExpressionSafe(expression, call); + + // then + assert.equal(result, "Will multiply 122 by 7 and return 854."); + assert.equal(result2, "Will multiply 122 by 7 and return 854."); + }); + + it('should evalute expression using input params', function () { + //given + var expression = "Will multiply `a` by 7 and return `a * 7`."; + var method = 'multiply'; + var abi = [{ + "name": "multiply", + "constant": false, + "type": "function", + "inputs": [{ + "name": "b", + "type": "uint256" + }], + "outputs": [{ + "name": "d", + "type": "uint256" + }] + }]; + + var transaction = { + "jsonrpc": "2.0", + "method": "eth_call", + "params": [{ + "to": "0x8521742d3f456bd237e312d6e30724960f72517a", + "data": "0xc6888fa1000000000000000000000000000000000000000000000000000000000000007a" + }], + "id": 6 + }; + + var call = { + method: method, + abi: abi, + transaction: transaction + }; + + // when + var exceptionThrow = function () { natspec.evaluateExpression(expression, call);} + var result = natspec.evaluateExpressionSafe(expression, call); + + // then + assert.equal(result, "Natspec evaluation failed, wrong input params"); + assert.throws(exceptionThrow, "Natspec evaluation failed, wrong input params"); + }); + + it('should evalute expression using input params', function () { + //given + var expression = "Will multiply `a` by 7 and return `a * 7`."; + var method = 'multiply2'; + var abi = [{ + "name": "multiply", + "constant": false, + "type": "function", + "inputs": [{ + "name": "a", + "type": "uint256" + }], + "outputs": [{ + "name": "d", + "type": "uint256" + }] + }]; + + var transaction = { + "jsonrpc": "2.0", + "method": "eth_call", + "params": [{ + "to": "0x8521742d3f456bd237e312d6e30724960f72517a", + "data": "0xc6888fa1000000000000000000000000000000000000000000000000000000000000007a" + }], + "id": 6 + }; + + var call = { + method: method, + abi: abi, + transaction: transaction + }; + + // when + var exceptionThrow = function () { natspec.evaluateExpression(expression, call);} + var result = natspec.evaluateExpressionSafe(expression, call); + + // then + assert.equal(result, "Natspec evaluation failed, method does not exist"); + assert.throws(exceptionThrow, "Natspec evaluation failed, method does not exist"); + }); +}); + + From 39e72eaa0cb806504f68e2df23436403e63c4d76 Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Mon, 9 Mar 2015 14:27:27 +0100 Subject: [PATCH 053/100] removed unused file --- libnatspec/natspec.js | 102 ----------------------------------------- libnatspec/natspec.qrc | 2 +- 2 files changed, 1 insertion(+), 103 deletions(-) delete mode 100644 libnatspec/natspec.js diff --git a/libnatspec/natspec.js b/libnatspec/natspec.js deleted file mode 100644 index ed30b5378..000000000 --- a/libnatspec/natspec.js +++ /dev/null @@ -1,102 +0,0 @@ - -/** - * This plugin exposes 'evaluateExpression' method which should be used - * to evaluate natspec description - */ - -/// Object which should be used by NatspecExpressionEvaluator -/// abi - abi of the contract that will be used -/// method - name of the method that is called -/// params - input params of the method that will be called -var globals = { - abi: [], - method: "", - params: [] -}; - -/// Helper method -/// Should be called to copy values from object to global context -var copyToContext = function (obj, context) { - var keys = Object.keys(obj); - keys.forEach(function (key) { - context[key] = obj[key]; - }); -} - -/// Helper method -/// Should be called to get method with given name from the abi -/// @param contract's abi -/// @param name of the method that we are looking for -var getMethodWithName = function(abi, name) { - for (var i = 0; i < abi.length; i++) { - if (abi[i].name === name) { - return abi[i]; - } - } - //console.warn('could not find method with name: ' + name); - return undefined; -}; - -/// Function called to get all contract's storage values -/// @returns hashmap with contract properties which are used -/// TODO: check if this function will be used -var getContractProperties = function (address, abi) { - return {}; -}; - -/// Function called to get all contract's methods -/// @returns hashmap with used contract's methods -/// TODO: check if this function will be used -var getContractMethods = function (address, abi) { - //return web3.eth.contract(address, abi); // commented out web3 usage - return {}; -}; - -/// Function called to get all contract method input variables -/// @returns hashmap with all contract's method input variables -var getMethodInputParams = function (method, params) { - return method.inputs.reduce(function (acc, current, index) { - acc[current.name] = params[index]; - return acc; - }, {}); -}; - -/// Should be called to evaluate single expression -/// Is internally using javascript's 'eval' method -/// Should be checked if it is safe -var evaluateExpression = function (expression) { - - var self = this; - - //var storage = getContractProperties(address, abi); - //var methods = getContractMethods(address, abi); - - var method = getMethodWithName(globals.abi, globals.method); - if (method) { - var input = getMethodInputParams(method, globals.params); - copyToContext(input, self); - } - - // TODO: test if it is safe - var evaluatedExpression = ""; - - // match everything in `` quotes - var pattern = /\`(?:\\.|[^`\\])*\`/gim - var match; - var lastIndex = 0; - while ((match = pattern.exec(expression)) !== null) { - var startIndex = pattern.lastIndex - match[0].length; - - var toEval = match[0].slice(1, match[0].length - 1); - - evaluatedExpression += expression.slice(lastIndex, startIndex); - evaluatedExpression += eval(toEval).toString(); - - lastIndex = pattern.lastIndex; - } - - evaluatedExpression += expression.slice(lastIndex); - - return evaluatedExpression; -}; - diff --git a/libnatspec/natspec.qrc b/libnatspec/natspec.qrc index db125974c..377b8d33e 100644 --- a/libnatspec/natspec.qrc +++ b/libnatspec/natspec.qrc @@ -1,5 +1,5 @@ - natspec.js + natspecjs/dist/natspec.min.js From b76622494e0564b28d2b70adcf159074cfb41a85 Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Mon, 9 Mar 2015 15:22:07 +0100 Subject: [PATCH 054/100] fixed natspec evaluation --- libnatspec/NatspecExpressionEvaluator.cpp | 20 +++++++++----------- libnatspec/NatspecExpressionEvaluator.h | 9 +++++---- test/natspec.cpp | 19 +++++++++++++++---- 3 files changed, 29 insertions(+), 19 deletions(-) diff --git a/libnatspec/NatspecExpressionEvaluator.cpp b/libnatspec/NatspecExpressionEvaluator.cpp index fd8369145..ab957295e 100644 --- a/libnatspec/NatspecExpressionEvaluator.cpp +++ b/libnatspec/NatspecExpressionEvaluator.cpp @@ -35,25 +35,23 @@ static QString contentsOfQResource(string const& _res) return in.readAll(); } -NatspecExpressionEvaluator::NatspecExpressionEvaluator(QString const& _abi, QString const& _method, QString const& _params) +NatspecExpressionEvaluator::NatspecExpressionEvaluator(QString const& _abi, QString const& _transaction, QString const& _method) +: m_abi(_abi), m_transaction(_transaction), m_method(_method) { Q_INIT_RESOURCE(natspec); QJSValue result = m_engine.evaluate(contentsOfQResource(":/natspec/natspec.js")); if (result.isError()) BOOST_THROW_EXCEPTION(FileError()); - - m_engine.evaluate("globals.abi = " + _abi); - m_engine.evaluate("globals.method = " + _method); - m_engine.evaluate("globals.params = " + _params); + + m_engine.evaluate("var natspec = require('natspec')"); } QString NatspecExpressionEvaluator::evalExpression(QString const& _expression) { - QJSValue result = m_engine.evaluate("evaluateExpression(\"" + _expression + "\")"); - if (result.isError()) - { - cerr << "Could not evaluate expression: \"" << _expression.toStdString() << "\"" << endl; - return _expression; - } + QString call = ""; + if (!m_abi.isEmpty() && !m_transaction.isEmpty() && !m_method.isEmpty()) + call = ", {abi:" + m_abi + ", transaction:" + m_transaction + ", method: '" + m_method + "' }"; + + QJSValue result = m_engine.evaluate("natspec.evaluateExpressionSafe(\"" + _expression + "\"" + call + ")"); return result.toString(); } diff --git a/libnatspec/NatspecExpressionEvaluator.h b/libnatspec/NatspecExpressionEvaluator.h index 2ea224027..4aca090d6 100644 --- a/libnatspec/NatspecExpressionEvaluator.h +++ b/libnatspec/NatspecExpressionEvaluator.h @@ -34,12 +34,10 @@ class NatspecExpressionEvaluator public: /// Construct natspec expression evaluator /// @params abi - contract's abi in json format, passed as string + /// @params transaction - json object containing transaction data /// @params method - name of the contract's method for which we evaluate the natspec. - /// If we want to use raw string, it should be passed with quotation marks eg. "\"helloWorld\"" - /// If we pass string "helloWorld", the value of the object with name "helloWorld" will be used - /// @params params - array of method input params, passed as string, objects in array should be /// javascript valid objects - NatspecExpressionEvaluator(QString const& _abi = "[]", QString const& _method = "", QString const& _params = "[]"); + NatspecExpressionEvaluator(QString const& _abi = "[]", QString const& _transaction = "{}", QString const& _method = ""); /// Should be called to evaluate natspec expression /// @params expression - natspec expression @@ -48,4 +46,7 @@ public: private: QJSEngine m_engine; + QString m_abi; + QString m_transaction; + QString m_method; }; diff --git a/test/natspec.cpp b/test/natspec.cpp index 8ba660418..cdcedca46 100644 --- a/test/natspec.cpp +++ b/test/natspec.cpp @@ -34,7 +34,7 @@ BOOST_AUTO_TEST_CASE(natspec_eval_function_exists) // given NatspecExpressionEvaluator e; // when - string result = e.evalExpression("`typeof evaluateExpression`").toStdString(); + string result = e.evalExpression("`typeof natspec.evaluateExpression`").toStdString(); // then BOOST_CHECK_EQUAL(result, "function"); } @@ -77,7 +77,7 @@ BOOST_AUTO_TEST_CASE(natspec_js_eval_input_params) // given char const* abi = R"([ { - "name": "f", + "name": "multiply", "constant": false, "type": "function", "inputs": [ @@ -94,7 +94,18 @@ BOOST_AUTO_TEST_CASE(natspec_js_eval_input_params) ] } ])"; - NatspecExpressionEvaluator e(abi, "'f'", "[4]"); + + char const* transaction = R"({ + "jsonrpc": "2.0", + "method": "eth_call", + "params": [{ + "to": "0x8521742d3f456bd237e312d6e30724960f72517a", + "data": "0xc6888fa10000000000000000000000000000000000000000000000000000000000000004" + }], + "id": 6 + })"; + + NatspecExpressionEvaluator e(abi, transaction , "multiply"); // when string result = e.evalExpression("Will multiply `a` by 7 and return `a * 7`.").toStdString(); // then @@ -108,7 +119,7 @@ BOOST_AUTO_TEST_CASE(natspec_js_eval_error) // when string result = e.evalExpression("`test(`").toStdString(); // then - BOOST_CHECK_EQUAL(result, "`test(`"); + BOOST_CHECK_EQUAL(result, "Natspec evaluation failed, wrong input params"); } BOOST_AUTO_TEST_SUITE_END() From d44f09c9eccd43b0a329c14c4328e68bbd576208 Mon Sep 17 00:00:00 2001 From: CJentzsch Date: Mon, 9 Mar 2015 16:44:23 +0100 Subject: [PATCH 055/100] add input limit tests --- test/vm.cpp | 64 +++++++++++++++++++++++++++++++---------------------- 1 file changed, 37 insertions(+), 27 deletions(-) diff --git a/test/vm.cpp b/test/vm.cpp index eb7c174ec..a39f0005f 100644 --- a/test/vm.cpp +++ b/test/vm.cpp @@ -533,33 +533,43 @@ BOOST_AUTO_TEST_CASE(vmPerformanceTest) } } -//BOOST_AUTO_TEST_CASE(vmInputLimitsTest1) -//{ -// for (int i = 1; i < boost::unit_test::framework::master_test_suite().argc; ++i) -// { -// string arg = boost::unit_test::framework::master_test_suite().argv[i]; -// if (arg == "--inputlimits" || arg == "--all") -// { -// auto start = chrono::steady_clock::now(); - -// dev::test::executeTests("vmInputLimitsTest1", "/VMTests", dev::test::doVMTests); - -// auto end = chrono::steady_clock::now(); -// auto duration(chrono::duration_cast(end - start)); -// cnote << "test duration: " << duration.count() << " milliseconds.\n"; -// } -// } -//} - -//BOOST_AUTO_TEST_CASE(vmInputLimitsTest2) -//{ -// for (int i = 1; i < boost::unit_test::framework::master_test_suite().argc; ++i) -// { -// string arg = boost::unit_test::framework::master_test_suite().argv[i]; -// if (arg == "--inputlimits" || arg == "--all") -// dev::test::executeTests("vmInputLimitsTest2", "/VMTests", dev::test::doVMTests); -// } -//} +BOOST_AUTO_TEST_CASE(vmInputLimitsTest1) +{ + for (int i = 1; i < boost::unit_test::framework::master_test_suite().argc; ++i) + { + string arg = boost::unit_test::framework::master_test_suite().argv[i]; + if (arg == "--inputlimits" || arg == "--all") + { + auto start = chrono::steady_clock::now(); + + dev::test::executeTests("vmInputLimits1", "/VMTests", dev::test::doVMTests); + + auto end = chrono::steady_clock::now(); + auto duration(chrono::duration_cast(end - start)); + cnote << "test duration: " << duration.count() << " milliseconds.\n"; + } + } +} + +BOOST_AUTO_TEST_CASE(vmInputLimitsTest2) +{ + for (int i = 1; i < boost::unit_test::framework::master_test_suite().argc; ++i) + { + string arg = boost::unit_test::framework::master_test_suite().argv[i]; + if (arg == "--inputlimits" || arg == "--all") + dev::test::executeTests("vmInputLimits2", "/VMTests", dev::test::doVMTests); + } +} + +BOOST_AUTO_TEST_CASE(vmInputLimitsLightTest) +{ + for (int i = 1; i < boost::unit_test::framework::master_test_suite().argc; ++i) + { + string arg = boost::unit_test::framework::master_test_suite().argv[i]; + if (arg == "--inputlimits" || arg == "--all") + dev::test::executeTests("vmInputLimitsLight", "/VMTests", dev::test::doVMTests); + } +} BOOST_AUTO_TEST_CASE(vmRandom) { From 1e47758cbe2839ef1f185fa67a1636f44da04fe3 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 9 Mar 2015 17:15:10 +0100 Subject: [PATCH 056/100] Make subroutine id non-random. --- libevmcore/Assembly.cpp | 20 +++++++++++--------- libevmcore/Assembly.h | 6 +++--- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/libevmcore/Assembly.cpp b/libevmcore/Assembly.cpp index ef4021d51..be35b62c2 100644 --- a/libevmcore/Assembly.cpp +++ b/libevmcore/Assembly.cpp @@ -99,6 +99,8 @@ void Assembly::append(Assembly const& _a) { if (i.type() == Tag || i.type() == PushTag) i.m_data += m_usedTags; + else if (i.type() == PushSub || i.type() == PushSubSize) + i.m_data += m_subs.size(); append(i); } m_deposit = newDeposit; @@ -108,7 +110,7 @@ void Assembly::append(Assembly const& _a) for (auto const& i: _a.m_strings) m_strings.insert(i); for (auto const& i: _a.m_subs) - m_subs.insert(i); + m_subs.push_back(i); assert(!_a.m_baseDeposit); assert(!_a.m_totalDeposit); @@ -245,12 +247,12 @@ ostream& Assembly::streamRLP(ostream& _out, string const& _prefix, StringMap con { _out << _prefix << ".data:" << endl; for (auto const& i: m_data) - if (!m_subs.count(i.first)) + if (u256(i.first) >= m_subs.size()) _out << _prefix << " " << hex << (unsigned)(u256)i.first << ": " << toHex(i.second) << endl; - for (auto const& i: m_subs) + for (size_t i = 0; i < m_subs.size(); ++i) { - _out << _prefix << " " << hex << (unsigned)(u256)i.first << ": " << endl; - i.second.streamRLP(_out, _prefix + " ", _sourceCodes); + _out << _prefix << " " << hex << i << ": " << endl; + m_subs[i].streamRLP(_out, _prefix + " ", _sourceCodes); } } return _out; @@ -493,8 +495,8 @@ Assembly& Assembly::optimise(bool _enable) copt << total << " optimisations done."; - for (auto& i: m_subs) - i.second.optimise(true); + for (auto& sub: m_subs) + sub.optimise(true); return *this; } @@ -511,8 +513,8 @@ bytes Assembly::assemble() const unsigned bytesPerTag = dev::bytesRequired(totalBytes); byte tagPush = (byte)Instruction::PUSH1 - 1 + bytesPerTag; - for (auto const& i: m_subs) - m_data[i.first] = i.second.assemble(); + for (size_t i = 0; i < m_subs.size(); ++i) + m_data[u256(i)] = m_subs[i].assemble(); unsigned bytesRequiredIncludingData = bytesRequired(); unsigned bytesPerDataRef = dev::bytesRequired(bytesRequiredIncludingData); diff --git a/libevmcore/Assembly.h b/libevmcore/Assembly.h index c3bc1bc55..cd71db747 100644 --- a/libevmcore/Assembly.h +++ b/libevmcore/Assembly.h @@ -81,9 +81,9 @@ public: AssemblyItem newTag() { return AssemblyItem(Tag, m_usedTags++); } AssemblyItem newPushTag() { return AssemblyItem(PushTag, m_usedTags++); } AssemblyItem newData(bytes const& _data) { h256 h = (u256)std::hash()(asString(_data)); m_data[h] = _data; return AssemblyItem(PushData, h); } - AssemblyItem newSub(Assembly const& _sub) { h256 h = h256::random(s_fixedHashEngine); m_subs[h] = _sub; return AssemblyItem(PushSub, h); } + AssemblyItem newSub(Assembly const& _sub) { m_subs.push_back(_sub); return AssemblyItem(PushSub, m_subs.size() - 1); } AssemblyItem newPushString(std::string const& _data) { h256 h = (u256)std::hash()(_data); m_strings[h] = _data; return AssemblyItem(PushString, h); } - AssemblyItem newPushSubSize(h256 const& _subId) { return AssemblyItem(PushSubSize, _subId); } + AssemblyItem newPushSubSize(u256 const& _subId) { return AssemblyItem(PushSubSize, _subId); } AssemblyItem append() { return append(newTag()); } void append(Assembly const& _a); @@ -134,7 +134,7 @@ protected: unsigned m_usedTags = 0; AssemblyItems m_items; mutable std::map m_data; - std::map m_subs; + std::vector m_subs; std::map m_strings; int m_deposit = 0; From 3292c58599b36cba2f32c3e0728851f6886db421 Mon Sep 17 00:00:00 2001 From: subtly Date: Mon, 9 Mar 2015 18:14:49 +0100 Subject: [PATCH 057/100] bugfix and doc clarification. fix test. --- libdevcrypto/CryptoPP.cpp | 7 ++++--- libdevcrypto/CryptoPP.h | 8 ++++---- libp2p/Host.h | 2 +- test/crypto.cpp | 2 +- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/libdevcrypto/CryptoPP.cpp b/libdevcrypto/CryptoPP.cpp index 77893736f..87da88742 100644 --- a/libdevcrypto/CryptoPP.cpp +++ b/libdevcrypto/CryptoPP.cpp @@ -319,13 +319,14 @@ bool Secp256k1::verifySecret(Secret const& _s, Public& _p) void Secp256k1::agree(Secret const& _s, Public const& _r, h256& o_s) { - (void)o_s; - (void)_s; + // TODO: mutex ASN1::secp256k1() singleton + // Creating Domain is non-const for m_oid and m_oid is not thread-safe ECDH::Domain d(ASN1::secp256k1()); assert(d.AgreedValueLength() == sizeof(o_s)); byte remote[65] = {0x04}; memcpy(&remote[1], _r.data(), 64); - assert(d.Agree(o_s.data(), _s.data(), remote)); + bool result = d.Agree(o_s.data(), _s.data(), remote); + assert(result); } void Secp256k1::exportPublicKey(CryptoPP::DL_PublicKey_EC const& _k, Public& o_p) diff --git a/libdevcrypto/CryptoPP.h b/libdevcrypto/CryptoPP.h index b673c44a6..4991e3713 100644 --- a/libdevcrypto/CryptoPP.h +++ b/libdevcrypto/CryptoPP.h @@ -76,16 +76,16 @@ public: void toPublic(Secret const& _s, Public& o_public) { exponentToPublic(Integer(_s.data(), sizeof(_s)), o_public); } - /// Encrypts text (replace input). (ECIES w/XOR) + /// Encrypts text (replace input). (ECIES w/XOR-SHA1) void encrypt(Public const& _k, bytes& io_cipher); - /// Decrypts text (replace input). (ECIES w/XOR) + /// Decrypts text (replace input). (ECIES w/XOR-SHA1) void decrypt(Secret const& _k, bytes& io_text); - /// Encrypts text (replace input). (ECIES w/AES128-CTR) + /// Encrypts text (replace input). (ECIES w/AES128-CTR-SHA256) void encryptECIES(Public const& _k, bytes& io_cipher); - /// Decrypts text (replace input). (ECIES w/AES128-CTR) + /// Decrypts text (replace input). (ECIES w/AES128-CTR-SHA256) bool decryptECIES(Secret const& _k, bytes& io_text); /// Key derivation function used by encryptECIES and decryptECIES. diff --git a/libp2p/Host.h b/libp2p/Host.h index ccb34b064..3b2ebe6aa 100644 --- a/libp2p/Host.h +++ b/libp2p/Host.h @@ -226,7 +226,7 @@ private: mutable RecursiveMutex x_sessions; std::list> m_connecting; ///< Pending connections. - Mutex x_connecting; + Mutex x_connecting; ///< Mutex for m_connecting. unsigned m_idealPeerCount = 5; ///< Ideal number of peers to be connected to. diff --git a/test/crypto.cpp b/test/crypto.cpp index 2d7121f89..dbbc2dfa0 100644 --- a/test/crypto.cpp +++ b/test/crypto.cpp @@ -299,7 +299,7 @@ BOOST_AUTO_TEST_CASE(ecies_standard) s_secp256k1.encryptECIES(k.pub(), b); BOOST_REQUIRE(b != asBytes(original)); - BOOST_REQUIRE(b.size() > 0 && ((u128)h128(b)) > 0); + BOOST_REQUIRE(b.size() > 0 && b[0] == 0x04); s_secp256k1.decryptECIES(k.sec(), b); BOOST_REQUIRE(bytesConstRef(&b).cropped(0, original.size()).toBytes() == asBytes(original)); From 80edf6163030f3bfe82b3c44d4531d71585831fe Mon Sep 17 00:00:00 2001 From: subtly Date: Mon, 9 Mar 2015 18:37:50 +0100 Subject: [PATCH 058/100] changes from code review --- libp2p/Host.h | 2 +- libp2p/Session.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libp2p/Host.h b/libp2p/Host.h index 3b2ebe6aa..219316c58 100644 --- a/libp2p/Host.h +++ b/libp2p/Host.h @@ -50,7 +50,7 @@ namespace dev namespace p2p { - + class Host; class HostNodeTableHandler: public NodeTableEventHandler diff --git a/libp2p/Session.cpp b/libp2p/Session.cpp index 5ceb61f8b..5caf5bd44 100644 --- a/libp2p/Session.cpp +++ b/libp2p/Session.cpp @@ -403,7 +403,7 @@ void Session::doRead() // ignore packets received while waiting to disconnect. if (m_dropped) return; - + auto self(shared_from_this()); ba::async_read(m_socket, boost::asio::buffer(m_data, h256::size), [this,self](boost::system::error_code ec, std::size_t length) { @@ -427,7 +427,7 @@ void Session::doRead() /// check frame size uint32_t frameSize = (m_data[0] * 256 + m_data[1]) * 256 + m_data[2]; - if (frameSize > 16777216) + if (frameSize >= (uint32_t)1 << 24) { clog(NetWarn) << "frame size too large"; drop(BadProtocol); From eeeb8421926406ac8d5cea0b4c947cb47f98fb18 Mon Sep 17 00:00:00 2001 From: subtly Date: Mon, 9 Mar 2015 18:42:03 +0100 Subject: [PATCH 059/100] update doc for disabled code --- libp2p/Host.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index e8e206233..8f4f8a69d 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -544,7 +544,11 @@ void Host::run(boost::system::error_code const&) pp->serviceNodesRequest(); keepAlivePeers(); -// disconnectLatePeers(); + + // At this time peers will be disconnected based on natural TCP timeout. + // disconnectLatePeers needs to be updated for the assumption that Session + // is always live and to ensure reputation and fallback timers are properly + // updated. // disconnectLatePeers(); if (peerCount() < m_idealPeerCount) { From 7d1de2b4022dfedd4c4380494ae074526b682fcd Mon Sep 17 00:00:00 2001 From: subtly Date: Mon, 9 Mar 2015 19:06:01 +0100 Subject: [PATCH 060/100] replace go counter logic. remove dead test code. --- libdevcrypto/CryptoPP.cpp | 8 +------- test/rlpx.cpp | 1 - 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/libdevcrypto/CryptoPP.cpp b/libdevcrypto/CryptoPP.cpp index 87da88742..11e4c1472 100644 --- a/libdevcrypto/CryptoPP.cpp +++ b/libdevcrypto/CryptoPP.cpp @@ -55,14 +55,8 @@ bytes Secp256k1::eciesKDF(Secret _z, bytes _s1, unsigned kdByteLen) k.reserve(k.size() + h256::size); move(digest.begin(), digest.end(), back_inserter(k)); - if (ctr[3]++ && ctr[3] != 0) + if (++ctr[3] || ++ctr[2] || ++ctr[1] || ++ctr[0]) continue; - else if (ctr[2]++ && ctr[2] != 0) - continue; - else if (ctr[1]++ && ctr[1] != 0) - continue; - else - ctr[0]++; } k.resize(kdByteLen); diff --git a/test/rlpx.cpp b/test/rlpx.cpp index 593613106..6a86652fb 100644 --- a/test/rlpx.cpp +++ b/test/rlpx.cpp @@ -363,7 +363,6 @@ BOOST_AUTO_TEST_CASE(test_secrets_from_go) // test decrypt of frame headers for recvHello bytes plaintext(16); m_frameDec.ProcessData(plaintext.data(), recvHello.data(), h128::size); - cout << "decrypt recv got: " << *(h128*)plaintext.data(); } From 4f852f473ef6eb5896a5460ffc937c80680ae62f Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 9 Mar 2015 19:22:43 +0100 Subject: [PATCH 061/100] Global variable "now" (alias for block.timestamp). --- libsolidity/ExpressionCompiler.cpp | 12 +++++++++++- libsolidity/GlobalContext.cpp | 1 + test/SolidityEndToEndTest.cpp | 11 +++++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp index b02aecf5f..d2457e676 100644 --- a/libsolidity/ExpressionCompiler.cpp +++ b/libsolidity/ExpressionCompiler.cpp @@ -825,10 +825,20 @@ void ExpressionCompiler::endVisit(Identifier const& _identifier) Declaration const* declaration = _identifier.getReferencedDeclaration(); if (MagicVariableDeclaration const* magicVar = dynamic_cast(declaration)) { - if (magicVar->getType()->getCategory() == Type::Category::Contract) + switch (magicVar->getType()->getCategory()) + { + case Type::Category::Contract: // "this" or "super" if (!dynamic_cast(*magicVar->getType()).isSuper()) m_context << eth::Instruction::ADDRESS; + break; + case Type::Category::Integer: + // "now" + m_context << eth::Instruction::TIMESTAMP; + break; + default: + break; + } } else if (FunctionDefinition const* functionDef = dynamic_cast(declaration)) m_context << m_context.getVirtualFunctionEntryLabel(*functionDef).pushTag(); diff --git a/libsolidity/GlobalContext.cpp b/libsolidity/GlobalContext.cpp index 60de5105f..411e99abb 100644 --- a/libsolidity/GlobalContext.cpp +++ b/libsolidity/GlobalContext.cpp @@ -37,6 +37,7 @@ GlobalContext::GlobalContext(): m_magicVariables(vector>{make_shared("block", make_shared(MagicType::Kind::Block)), make_shared("msg", make_shared(MagicType::Kind::Message)), make_shared("tx", make_shared(MagicType::Kind::Transaction)), + make_shared("now", make_shared(256)), make_shared("suicide", make_shared(strings{"address"}, strings{}, FunctionType::Location::Suicide)), make_shared("sha3", diff --git a/test/SolidityEndToEndTest.cpp b/test/SolidityEndToEndTest.cpp index ae2417052..3205c038a 100644 --- a/test/SolidityEndToEndTest.cpp +++ b/test/SolidityEndToEndTest.cpp @@ -1031,6 +1031,17 @@ BOOST_AUTO_TEST_CASE(blockchain) BOOST_CHECK(callContractFunctionWithValue("someInfo()", 28) == encodeArgs(28, 0, 1)); } +BOOST_AUTO_TEST_CASE(now) +{ + char const* sourceCode = "contract test {\n" + " function someInfo() returns (bool success) {\n" + " return block.timestamp == now && now > 0;\n" + " }\n" + "}\n"; + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction("someInfo()") == encodeArgs(true)); +} + BOOST_AUTO_TEST_CASE(function_types) { char const* sourceCode = "contract test {\n" From f1efb7c3f9e3034fa9bea6421698505e2f06bf0c Mon Sep 17 00:00:00 2001 From: subtly Date: Mon, 9 Mar 2015 19:39:39 +0100 Subject: [PATCH 062/100] fix coding standards --- libp2p/Session.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libp2p/Session.cpp b/libp2p/Session.cpp index 5caf5bd44..2c6530a2d 100644 --- a/libp2p/Session.cpp +++ b/libp2p/Session.cpp @@ -310,7 +310,7 @@ void Session::send(bytes&& _msg) if (!m_socket.is_open()) return; - + bool doWrite = false; { Guard l(x_writeQueue); From 5871652d9078d0f3b15fb1586ad66d05ffaaeeda Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 9 Mar 2015 22:11:16 +0100 Subject: [PATCH 063/100] Fixed dockerfile. --- solc/docker_emscripten/Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/solc/docker_emscripten/Dockerfile b/solc/docker_emscripten/Dockerfile index b593cbf74..06467a2c5 100644 --- a/solc/docker_emscripten/Dockerfile +++ b/solc/docker_emscripten/Dockerfile @@ -10,6 +10,7 @@ RUN apt-get install -qy automake libtool yasm scons RUN useradd -ms /bin/bash user USER user +ENV HOME /home/user WORKDIR /home/user # Emscripten SDK From e0fa87db76277189ecc341f2564b7775946fb6d0 Mon Sep 17 00:00:00 2001 From: yann300 Date: Tue, 10 Mar 2015 11:37:29 +0100 Subject: [PATCH 064/100] small changes --- mix/qml/LogsPane.qml | 50 ++++++++++++++++++++++++++--- mix/qml/MainContent.qml | 20 ++++++------ mix/qml/StatusPane.qml | 70 ++++++++++++++++++++--------------------- mix/qml/WebPreview.qml | 16 +++------- 4 files changed, 95 insertions(+), 61 deletions(-) diff --git a/mix/qml/LogsPane.qml b/mix/qml/LogsPane.qml index ca78b3854..67bdd44c1 100644 --- a/mix/qml/LogsPane.qml +++ b/mix/qml/LogsPane.qml @@ -19,6 +19,7 @@ Rectangle border.color: "#808080" border.width: 1 ColumnLayout { + z: 2 height: parent.height width: parent.width spacing: 0 @@ -152,6 +153,31 @@ Rectangle } } + ToolButton { + id: compilationButton + checkable: true + height: 30 + anchors.verticalCenter: parent.verticalCenter + checked: false + onCheckedChanged: { + proxyModel.toogleFilter("compilation") + } + tooltip: qsTr("Compilation") + style: + ButtonStyle { + label: + Item { + DefaultLabel { + font.family: "sans serif" + font.pointSize: Style.absoluteSize(-3) + color: "#5391d8" + anchors.centerIn: parent + text: qsTr("Compilation") + } + } + } + } + DefaultTextField { id: searchBox @@ -160,6 +186,7 @@ Rectangle width: 200 font.family: "sans serif" font.pointSize: Style.absoluteSize(-3) + font.italic: true onTextChanged: { proxyModel.search(text); } @@ -168,7 +195,6 @@ Rectangle ListModel { id: logsModel - } TableView { @@ -180,7 +206,8 @@ Rectangle onDoubleClicked: { var log = logsModel.get((logsTable.currentRow)); - appContext.toClipboard(log.type + " " + log.level + " " + log.date + " " + log.content); + if (log) + appContext.toClipboard(log.type + "\t" + log.level + "\t" + log.date + "\t" + log.content); } model: SortFilterProxyModel { @@ -216,9 +243,9 @@ Rectangle function regEx(_value) { - console.log("(?:" + roles.join('|') + ")"); return "(?:" + roles.join('|') + ")"; } + filterType: "(?:javascript|run|state)" filterContent: "" filterSyntax: SortFilterProxyModel.RegExp @@ -235,7 +262,7 @@ Rectangle { role: "type" title: qsTr("type") - width: 100 + width: 80 delegate: itemDelegate } TableViewColumn @@ -245,6 +272,14 @@ Rectangle width: 700 delegate: itemDelegate } + + rowDelegate: Item { + Rectangle { + width: logsTable.width - 4 + height: 17 + color: styleData.alternate ? "transparent" : "#f0f0f0" + } + } } Component { @@ -253,7 +288,12 @@ Rectangle text: styleData.value; font.family: "sans serif" font.pointSize: Style.absoluteSize(-1) - color: "#808080" + color: { + if (proxyModel.get(styleData.row).level === "error") + return "red" + else + return "#808080" + } } } } diff --git a/mix/qml/MainContent.qml b/mix/qml/MainContent.qml index d295e9da0..7ac751a79 100644 --- a/mix/qml/MainContent.qml +++ b/mix/qml/MainContent.qml @@ -35,8 +35,8 @@ Rectangle { onCompilationComplete: { if (firstCompile) { firstCompile = false; - if (runOnProjectLoad) - startQuickDebugging(); + if (runOnProjectLoad) + startQuickDebugging(); } } } @@ -102,7 +102,6 @@ Rectangle { } CodeEditorExtensionManager { - //headerView: headerPaneTabs; } Settings { @@ -116,6 +115,7 @@ Rectangle { ColumnLayout { + id: mainColumn anchors.fill: parent spacing: 0 Rectangle { @@ -141,7 +141,7 @@ Rectangle { } } - Rectangle{ + Rectangle { Layout.fillWidth: true height: 1 color: "#8c8c8c" @@ -162,9 +162,9 @@ Rectangle { { anchors.fill: parent handleDelegate: Rectangle { - width: 1 - height: 1 - color: "#8c8c8c" + width: 1 + height: 1 + color: "#8c8c8c" } orientation: Qt.Horizontal @@ -174,16 +174,18 @@ Rectangle { Layout.minimumWidth: 250 Layout.fillHeight: true } + Rectangle { id: contentView Layout.fillHeight: true Layout.fillWidth: true + SplitView { - handleDelegate: Rectangle { + handleDelegate: Rectangle { width: 1 height: 1 color: "#8c8c8c" - } + } id: codeWebSplitter anchors.fill: parent orientation: Qt.Vertical diff --git a/mix/qml/StatusPane.qml b/mix/qml/StatusPane.qml index 99ab39b6c..1ce7917a9 100644 --- a/mix/qml/StatusPane.qml +++ b/mix/qml/StatusPane.qml @@ -24,6 +24,7 @@ Rectangle { var errorInfo = ErrorLocationFormater.extractErrorInfo(message, true); status.text = errorInfo.errorLocation + " " + errorInfo.errorDetail; debugImg.state = ""; + errorMessage(status.text, "Compilation"); } debugRunActionIcon.enabled = codeModel.hasContract; } @@ -51,17 +52,27 @@ Rectangle { Connections { target: webPreview - onJavaScriptErrorMessage: errorMessage(_content, "javascript") - onJavaScriptWarningMessage: warningMessage(_content, "javascript") - onJavaScriptInfoMessage: infoMessage(_content, "javascript") + onJavaScriptMessage: + { + if (_level === 0) + infoMessage(_content, "JavaScript") + else + { + var message = _sourceId.substring(_sourceId.lastIndexOf("/") + 1) + " - " + qsTr("line") + " " + _lineNb + " - " + _content; + if (_level === 1) + warningMessage(message, "JavaScript") + else if (_level === 2) + errorMessage(message, "JavaScript") + } + } } Connections { target:clientModel - onRunStarted: infoMessage(qsTr("Running transactions..."), "run"); - onRunFailed: errorMessage(format(_message), "run"); - onRunComplete: infoMessage(qsTr("Run complete"), "run"); - onNewBlock: infoMessage(qsTr("New block created"), "state"); + onRunStarted: infoMessage(qsTr("Running transactions..."), "Run"); + onRunFailed: errorMessage(format(_message), "Run"); + onRunComplete: infoMessage(qsTr("Run complete"), "Run"); + onNewBlock: infoMessage(qsTr("New block created"), "State"); function format(_message) { @@ -72,16 +83,16 @@ Rectangle { return _message; var exceptionInfos = _message.match(/(?:tag_)(.+)/g); for (var k in exceptionInfos) - formatted += " " + exceptionInfos[k].replace("*]", "").replace("tag_", "") + " - "; + formatted += " " + exceptionInfos[k].replace("*]", "").replace("tag_", "").replace("=", ""); return formatted; } } Connections { target:projectModel - onDeploymentStarted: infoMessage(qsTr("Running deployment..."), "deployment"); - onDeploymentError: errorMessage(error, "deployment"); - onDeploymentComplete: infoMessage(qsTr("Deployment complete"), "deployment"); - onDeploymentStepChanged: infoMessage(message, "deployment"); + onDeploymentStarted: infoMessage(qsTr("Running deployment..."), "Deployment"); + onDeploymentError: errorMessage(error, "Deployment"); + onDeploymentComplete: infoMessage(qsTr("Deployment complete"), "Deployment"); + onDeploymentStepChanged: infoMessage(message, "Deployment"); } Connections { target: codeModel @@ -95,7 +106,7 @@ Rectangle { Rectangle { id: statusContainer anchors.horizontalCenter: parent.horizontalCenter - anchors.verticalCenter: parent.verticalCenterw + anchors.verticalCenter: parent.verticalCenter radius: 3 width: 500 height: 30 @@ -171,6 +182,12 @@ Rectangle { color: "transparent" } } + MouseArea { + anchors.fill: parent + onClicked: { + logsContainer.toggle(); + } + } } Action { @@ -189,15 +206,17 @@ Rectangle { } else { - statusContainer.state = "logsOpened"; + statusContainer.state = "'logsOpened"; logsContainer.state = "opened"; + logsContainer.focus = true; + forceActiveFocus(); } } id: logsContainer width: 1000 height: 0 - //anchors.topMargin: + anchors.topMargin: 10 anchors.top: statusContainer.bottom anchors.horizontalCenter: parent.horizontalCenter visible: false @@ -233,26 +252,6 @@ Rectangle { } } - Button - { - id: logslink - anchors.left: statusContainer.right - anchors.leftMargin: 9 - anchors.verticalCenter: parent.verticalCenter - action: displayLogAction - iconSource: "qrc:/qml/img/search_filled.png" - } - - Action { - id: displayLogAction - tooltip: qsTr("Display Log") - onTriggered: { - logsContainer.toggle(); - //if (status.state === "error" && logPane.front().type === "run") - // mainContent.displayCompilationErrorIfAny(); - } - } - Rectangle { color: "transparent" @@ -267,7 +266,6 @@ Rectangle { { color: "transparent" anchors.fill: parent - Button { anchors.right: parent.right diff --git a/mix/qml/WebPreview.qml b/mix/qml/WebPreview.qml index 5a9673a2c..ba2975453 100644 --- a/mix/qml/WebPreview.qml +++ b/mix/qml/WebPreview.qml @@ -12,9 +12,7 @@ Item { id: webPreview property string pendingPageUrl: "" property bool initialized: false - signal javaScriptErrorMessage(string _content) - signal javaScriptWarningMessage(string _content) - signal javaScriptInfoMessage(string _content) + signal javaScriptMessage(var _level, string _sourceId, var _lineNb, string _content) function setPreviewUrl(url) { if (!initialized) @@ -201,7 +199,6 @@ Item { { setPreviewUrl(text); } - focus: true } @@ -219,7 +216,9 @@ Item { anchors.verticalCenter: parent.verticalCenter width: 21 height: 21 + focus: true } + CheckBox { id: autoReloadOnSave checked: true @@ -230,6 +229,7 @@ Item { text: qsTr("Auto reload on save") } } + focus: true } } } @@ -243,13 +243,7 @@ Item { id: webView experimental.settings.localContentCanAccessRemoteUrls: true onJavaScriptConsoleMessage: { - var info = sourceID + ":" + lineNumber + ":" + message; - if (level === 0) - webPreview.javaScriptInfoMessage(info); - else if (level === 1) - webPreview.javaScriptErrorMessage(info); - else if (level === 2) - webPreview.javaScriptErrorMessage(info); + webPreview.javaScriptMessage(level, sourceID, lineNumber, message); } onLoadingChanged: { if (!loading) { From 21cef78720c97244ef5ebfb1e7d44b96d3370426 Mon Sep 17 00:00:00 2001 From: yann300 Date: Tue, 10 Mar 2015 11:52:45 +0100 Subject: [PATCH 065/100] Modify SortFilterProxyModel to be compliant with Coding Standards --- mix/sortfilterproxymodel.cpp | 90 +++++++++--------------------------- mix/sortfilterproxymodel.h | 35 ++++++-------- 2 files changed, 37 insertions(+), 88 deletions(-) diff --git a/mix/sortfilterproxymodel.cpp b/mix/sortfilterproxymodel.cpp index 04e403738..62d85cfa3 100644 --- a/mix/sortfilterproxymodel.cpp +++ b/mix/sortfilterproxymodel.cpp @@ -44,7 +44,7 @@ using namespace dev::mix; -SortFilterProxyModel::SortFilterProxyModel(QObject *parent) : QSortFilterProxyModel(parent) +SortFilterProxyModel::SortFilterProxyModel(QObject* _parent) : QSortFilterProxyModel(_parent) { connect(this, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SIGNAL(countChanged())); connect(this, SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SIGNAL(countChanged())); @@ -55,14 +55,14 @@ int SortFilterProxyModel::count() const return rowCount(); } -QObject *SortFilterProxyModel::source() const +QObject* SortFilterProxyModel::source() const { return sourceModel(); } -void SortFilterProxyModel::setSource(QObject *source) +void SortFilterProxyModel::setSource(QObject* _source) { - setSourceModel(qobject_cast(source)); + setSourceModel(qobject_cast(_source)); } QByteArray SortFilterProxyModel::sortRole() const @@ -70,34 +70,24 @@ QByteArray SortFilterProxyModel::sortRole() const return roleNames().value(QSortFilterProxyModel::sortRole()); } -void SortFilterProxyModel::setSortRole(const QByteArray &role) +void SortFilterProxyModel::setSortRole(QByteArray const& _role) { - QSortFilterProxyModel::setSortRole(roleKey(role)); + QSortFilterProxyModel::setSortRole(roleKey(_role)); } -void SortFilterProxyModel::setSortOrder(Qt::SortOrder order) +void SortFilterProxyModel::setSortOrder(Qt::SortOrder _order) { - QSortFilterProxyModel::sort(0, order); + QSortFilterProxyModel::sort(0, _order); } -/*QByteArray SortFilterProxyModel::filterRole() const -{ - return roleNames().value(QSortFilterProxyModel::filterRole()); -}*/ - -/*void SortFilterProxyModel::setFilterRole(const QByteArray &role) -{ - QSortFilterProxyModel::setFilterRole(roleKey(role)); -}*/ - QString SortFilterProxyModel::filterString() const { return filterRegExp().pattern(); } -void SortFilterProxyModel::setFilterString(const QString &filter) +void SortFilterProxyModel::setFilterString(QString const& _filter) { - setFilterRegExp(QRegExp(filter, filterCaseSensitivity(), static_cast(filterSyntax()))); + setFilterRegExp(QRegExp(_filter, filterCaseSensitivity(), static_cast(filterSyntax()))); } SortFilterProxyModel::FilterSyntax SortFilterProxyModel::filterSyntax() const @@ -105,33 +95,33 @@ SortFilterProxyModel::FilterSyntax SortFilterProxyModel::filterSyntax() const return static_cast(filterRegExp().patternSyntax()); } -void SortFilterProxyModel::setFilterSyntax(SortFilterProxyModel::FilterSyntax syntax) +void SortFilterProxyModel::setFilterSyntax(SortFilterProxyModel::FilterSyntax _syntax) { - setFilterRegExp(QRegExp(filterString(), filterCaseSensitivity(), static_cast(syntax))); + setFilterRegExp(QRegExp(filterString(), filterCaseSensitivity(), static_cast(_syntax))); } -QJSValue SortFilterProxyModel::get(int idx) const +QJSValue SortFilterProxyModel::get(int _idx) const { QJSEngine *engine = qmlEngine(this); QJSValue value = engine->newObject(); - if (idx >= 0 && idx < count()) { + if (_idx >= 0 && _idx < count()) { QHash roles = roleNames(); QHashIterator it(roles); while (it.hasNext()) { it.next(); - value.setProperty(QString::fromUtf8(it.value()), data(index(idx, 0), it.key()).toString()); + value.setProperty(QString::fromUtf8(it.value()), data(index(_idx, 0), it.key()).toString()); } } return value; } -int SortFilterProxyModel::roleKey(const QByteArray &role) const +int SortFilterProxyModel::roleKey(QByteArray const& _role) const { QHash roles = roleNames(); QHashIterator it(roles); while (it.hasNext()) { it.next(); - if (it.value() == role) + if (it.value() == _role) return it.key(); } return -1; @@ -139,62 +129,26 @@ int SortFilterProxyModel::roleKey(const QByteArray &role) const QHash SortFilterProxyModel::roleNames() const { - if (QAbstractItemModel *source = sourceModel()) + if (QAbstractItemModel* source = sourceModel()) return source->roleNames(); return QHash(); } -bool SortFilterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const +bool SortFilterProxyModel::filterAcceptsRow(int _sourceRow, QModelIndex const& _sourceParent) const { - /*QRegExp rx = filterRegExp(); - if (rx.isEmpty()) - return true; - QAbstractItemModel *model = sourceModel(); - - if (filterRole().isEmpty()) { - QHash roles = roleNames(); - QHashIterator it(roles); - while (it.hasNext()) { - it.next(); - QModelIndex sourceIndex = model->index(sourceRow, 0, sourceParent); - QString key = model->data(sourceIndex, it.key()).toString(); - if (key.contains(rx))data - return true; - } - return false; - }*/ QRegExp rx = filterRegExp(); QAbstractItemModel *model = sourceModel(); - QModelIndex sourceIndex = model->index(sourceRow, 0, sourceParent); + QModelIndex sourceIndex = model->index(_sourceRow, 0, _sourceParent); if (!sourceIndex.isValid()) return true; QString keyType = model->data(sourceIndex, roleKey(type.toUtf8())).toString(); QString keyContent = model->data(sourceIndex, roleKey(content.toUtf8())).toString(); - return keyType.contains(m_filterType) && keyContent.contains(m_filterContent); -/* - for (auto filter: filterRoles()) - { - QString key = model->data(sourceIndex, roleKey(filter.toUtf8())).toString(); - if (!key.contains(rx)) - return false; - } - return true; - QModelIndex sourceIndex = model->index(sourceRow, 0, sourceParent); - if (!sourceIndex.isValid()) - return true; - for (auto role: filterR) - { - QString key = model->data(sourceIndex, roleKey(role)).toString(); - if (key.contains(rx)) - return true; - } - return false;*/ } -void SortFilterProxyModel::setFilterType(const QString &_type) +void SortFilterProxyModel::setFilterType(QString const& _type) { m_filterType = QRegExp(_type, filterCaseSensitivity(), static_cast(filterSyntax())); setFilterRegExp(_type); @@ -205,7 +159,7 @@ QString SortFilterProxyModel::filterType() const return m_filterType.pattern(); } -void SortFilterProxyModel::setFilterContent(const QString &_content) +void SortFilterProxyModel::setFilterContent(QString const& _content) { m_filterContent = QRegExp(_content, filterCaseSensitivity(), static_cast(filterSyntax())); setFilterRegExp(_content); diff --git a/mix/sortfilterproxymodel.h b/mix/sortfilterproxymodel.h index 8eae97a68..bc220a911 100644 --- a/mix/sortfilterproxymodel.h +++ b/mix/sortfilterproxymodel.h @@ -38,8 +38,7 @@ ** ****************************************************************************/ -#ifndef SORTFILTERPROXYMODEL_H -#define SORTFILTERPROXYMODEL_H +#pragma once #include #include @@ -49,11 +48,11 @@ namespace dev namespace mix { -class SortFilterProxyModel : public QSortFilterProxyModel +class SortFilterProxyModel: public QSortFilterProxyModel { Q_OBJECT Q_PROPERTY(int count READ count NOTIFY countChanged) - Q_PROPERTY(QObject *source READ source WRITE setSource) + Q_PROPERTY(QObject* source READ source WRITE setSource) Q_PROPERTY(QByteArray sortRole READ sortRole WRITE setSortRole) Q_PROPERTY(Qt::SortOrder sortOrder READ sortOrder WRITE setSortOrder) @@ -66,26 +65,23 @@ class SortFilterProxyModel : public QSortFilterProxyModel Q_ENUMS(FilterSyntax) public: - explicit SortFilterProxyModel(QObject *parent = 0); + explicit SortFilterProxyModel(QObject* _parent = 0); - QObject *source() const; - void setSource(QObject *source); + QObject* source() const; + void setSource(QObject* _source); QByteArray sortRole() const; - void setSortRole(const QByteArray &role); + void setSortRole(QByteArray const& _role); - void setSortOrder(Qt::SortOrder order); + void setSortOrder(Qt::SortOrder _order); QString filterContent() const; - void setFilterContent(const QString &_content); + void setFilterContent(QString const& _content); QString filterType() const; - void setFilterType(const QString &_type); + void setFilterType(QString const& _type); - /*QStringList filterRoles() const; - void setFilterRoles(const QStringList &roles); -*/ QString filterString() const; - void setFilterString(const QString &filter); + void setFilterString(QString const& _filter); enum FilterSyntax { RegExp, @@ -94,18 +90,18 @@ public: }; FilterSyntax filterSyntax() const; - void setFilterSyntax(FilterSyntax syntax); + void setFilterSyntax(FilterSyntax _syntax); int count() const; - Q_INVOKABLE QJSValue get(int index) const; + Q_INVOKABLE QJSValue get(int _index) const; signals: void countChanged(); protected: - int roleKey(const QByteArray &role) const; + int roleKey(QByteArray const& _role) const; QHash roleNames() const; - bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const; + bool filterAcceptsRow(int _sourceRow, QModelIndex const& _sourceParent) const; private: QRegExp m_filterType; @@ -116,4 +112,3 @@ private: } } -#endif // SORTFILTERPROXYMODEL_H From c7f703cc71e618e11f0c166c8e7cad0105d8351a Mon Sep 17 00:00:00 2001 From: yann300 Date: Tue, 10 Mar 2015 11:58:33 +0100 Subject: [PATCH 066/100] small changes --- mix/CodeEditorExtensionManager.cpp | 9 --------- mix/CodeEditorExtensionManager.h | 2 -- 2 files changed, 11 deletions(-) diff --git a/mix/CodeEditorExtensionManager.cpp b/mix/CodeEditorExtensionManager.cpp index 516184cff..7f1f5d216 100644 --- a/mix/CodeEditorExtensionManager.cpp +++ b/mix/CodeEditorExtensionManager.cpp @@ -51,14 +51,6 @@ void CodeEditorExtensionManager::loadEditor(QQuickItem* _editor) return; } -void CodeEditorExtensionManager::initExtensions() -{ - //std::shared_ptr output = std::make_shared(m_appContext); - //QObject::connect(m_appContext->codeModel(), &CodeModel::compilationComplete, this, &CodeEditorExtensionManager::applyCodeHighlight); - - //initExtension(output); -} - void CodeEditorExtensionManager::initExtension(std::shared_ptr _ext) { if (!_ext->contentUrl().isEmpty()) @@ -93,5 +85,4 @@ void CodeEditorExtensionManager::setRightView(QQuickItem* _rightView) void CodeEditorExtensionManager::setHeaderView(QQuickItem* _headerView) { m_headerView = _headerView; - initExtensions(); //TODO: move this to a proper place } diff --git a/mix/CodeEditorExtensionManager.h b/mix/CodeEditorExtensionManager.h index fe6fbb33a..ebfe2d8a3 100644 --- a/mix/CodeEditorExtensionManager.h +++ b/mix/CodeEditorExtensionManager.h @@ -49,8 +49,6 @@ class CodeEditorExtensionManager: public QObject public: CodeEditorExtensionManager(); ~CodeEditorExtensionManager(); - /// Initialize all extensions. - void initExtensions(); /// Initialize extension. void initExtension(std::shared_ptr); /// Set current tab view From 97350c88ed1c4e607691a7ec2afe7f17d226a0de Mon Sep 17 00:00:00 2001 From: Liana Husikyan Date: Thu, 5 Mar 2015 15:51:26 +0100 Subject: [PATCH 067/100] Added JumpType indicator to AssemblyItem. --- libevmcore/Assembly.cpp | 16 +++++++++++++++- libevmcore/Assembly.h | 9 ++++++++- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/libevmcore/Assembly.cpp b/libevmcore/Assembly.cpp index be35b62c2..c253ef68f 100644 --- a/libevmcore/Assembly.cpp +++ b/libevmcore/Assembly.cpp @@ -77,6 +77,20 @@ int AssemblyItem::deposit() const return 0; } +string AssemblyItem::getJumpTypeAsString() const +{ + switch (m_jumpType) + { + case JumpType::IntoFunction: + return "->"; + case JumpType::OutOfFunction: + return "<-"; + case JumpType::Ordinary: + default: + return "->"; + } +} + unsigned Assembly::bytesRequired() const { for (unsigned br = 1;; ++br) @@ -205,7 +219,7 @@ ostream& Assembly::streamRLP(ostream& _out, string const& _prefix, StringMap con switch (i.m_type) { case Operation: - _out << " " << instructionInfo((Instruction)(byte)i.m_data).name; + _out << " " << instructionInfo((Instruction)(byte)i.m_data).name << " " << i.getJumpTypeAsString( ) << endl; break; case Push: _out << " PUSH " << i.m_data; diff --git a/libevmcore/Assembly.h b/libevmcore/Assembly.h index cd71db747..8952deffd 100644 --- a/libevmcore/Assembly.h +++ b/libevmcore/Assembly.h @@ -42,6 +42,8 @@ class AssemblyItem friend class Assembly; public: + enum class JumpType { Ordinary, IntoFunction, OutOfFunction }; + AssemblyItem(u256 _push): m_type(Push), m_data(_push) {} AssemblyItem(Instruction _i): m_type(Operation), m_data((byte)_i) {} AssemblyItem(AssemblyItemType _type, u256 _data = 0): m_type(_type), m_data(_data) {} @@ -58,13 +60,18 @@ public: int deposit() const; bool match(AssemblyItem const& _i) const { return _i.m_type == UndefinedItem || (m_type == _i.m_type && (m_type != Operation || m_data == _i.m_data)); } - void setLocation(SourceLocation const& _location) { m_location = _location;} + void setLocation(SourceLocation const& _location) { m_location = _location; } SourceLocation const& getLocation() const { return m_location; } + void setJumpType(JumpType _jumpType) { m_jumpType = _jumpType; } + JumpType getJumpType() const { return m_jumpType; } + std::string getJumpTypeAsString() const; + private: AssemblyItemType m_type; u256 m_data; SourceLocation m_location; + JumpType m_jumpType; }; using AssemblyItems = std::vector; From f7841a9b49a42f0ead5786424e4372f75b354ed3 Mon Sep 17 00:00:00 2001 From: Liana Husikyan Date: Mon, 9 Mar 2015 13:15:59 +0100 Subject: [PATCH 068/100] fixed new line for user input from terminal. --- solc/CommandLineInterface.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 08acb5ebd..01d8f8485 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -272,7 +272,7 @@ bool CommandLineInterface::processInput() while (!cin.eof()) { getline(cin, s); - m_sourceCodes[""].append(s); + m_sourceCodes[""].append(s + '\n'); } } else From 4323a0c65508a3dc72bc5264fc78ca127894ccc9 Mon Sep 17 00:00:00 2001 From: Liana Husikyan Date: Mon, 9 Mar 2015 19:22:24 +0100 Subject: [PATCH 069/100] added information about jump type for jump instructions Conflicts: libevmcore/Assembly.cpp libsolidity/Compiler.cpp --- libevmcore/Assembly.cpp | 10 +++++----- libsolidity/Compiler.cpp | 11 ++++++++--- libsolidity/CompilerContext.cpp | 7 +++++++ libsolidity/CompilerContext.h | 2 +- libsolidity/ExpressionCompiler.cpp | 5 +++-- 5 files changed, 24 insertions(+), 11 deletions(-) diff --git a/libevmcore/Assembly.cpp b/libevmcore/Assembly.cpp index c253ef68f..57e124fb8 100644 --- a/libevmcore/Assembly.cpp +++ b/libevmcore/Assembly.cpp @@ -82,12 +82,12 @@ string AssemblyItem::getJumpTypeAsString() const switch (m_jumpType) { case JumpType::IntoFunction: - return "->"; + return "in"; case JumpType::OutOfFunction: - return "<-"; + return "out"; case JumpType::Ordinary: default: - return "->"; + return ""; } } @@ -219,7 +219,7 @@ ostream& Assembly::streamRLP(ostream& _out, string const& _prefix, StringMap con switch (i.m_type) { case Operation: - _out << " " << instructionInfo((Instruction)(byte)i.m_data).name << " " << i.getJumpTypeAsString( ) << endl; + _out << " " << instructionInfo((Instruction)(byte)i.m_data).name << "\t\t" << i.getJumpTypeAsString(); break; case Push: _out << " PUSH " << i.m_data; @@ -254,7 +254,7 @@ ostream& Assembly::streamRLP(ostream& _out, string const& _prefix, StringMap con default: BOOST_THROW_EXCEPTION(InvalidOpcode()); } - _out << string("\t\t") << getLocationFromSources(_sourceCodes, i.getLocation()) << endl; + _out << "\t\t" << getLocationFromSources(_sourceCodes, i.getLocation()) << endl; } if (!m_data.empty() || !m_subs.empty()) diff --git a/libsolidity/Compiler.cpp b/libsolidity/Compiler.cpp index 7ff846bdb..46888683e 100644 --- a/libsolidity/Compiler.cpp +++ b/libsolidity/Compiler.cpp @@ -177,7 +177,9 @@ void Compiler::appendFunctionSelector(ContractDefinition const& _contract) { callDataUnpackerEntryPoints.insert(std::make_pair(it.first, m_context.newTag())); m_context << eth::dupInstruction(1) << u256(FixedHash<4>::Arith(it.first)) << eth::Instruction::EQ; - m_context.appendConditionalJumpTo(callDataUnpackerEntryPoints.at(it.first)); + auto assemblyItem = callDataUnpackerEntryPoints.at(it.first); + //assemblyItem.setJumpType(eth::AssemblyItem::JumpType::IntoFunction); + m_context.appendConditionalJumpTo(assemblyItem); } if (FunctionDefinition const* fallback = _contract.getFallbackFunction()) { @@ -197,7 +199,9 @@ void Compiler::appendFunctionSelector(ContractDefinition const& _contract) m_context << callDataUnpackerEntryPoints.at(it.first); eth::AssemblyItem returnTag = m_context.pushNewTag(); appendCalldataUnpacker(functionType->getParameterTypes()); - m_context.appendJumpTo(m_context.getFunctionEntryLabel(functionType->getDeclaration())); + auto assemblyItem = m_context.getFunctionEntryLabel(functionType->getDeclaration()); + //assemblyItem.setJumpType(eth::AssemblyItem::JumpType::IntoFunction); + m_context.appendJumpTo(assemblyItem); m_context << returnTag; appendReturnValuePacker(functionType->getReturnParameterTypes()); } @@ -378,8 +382,9 @@ bool Compiler::visit(FunctionDefinition const& _function) m_context.removeVariable(*localVariable); m_context.adjustStackOffset(-(int)c_returnValuesSize); + if (!_function.isConstructor()) - m_context << eth::Instruction::JUMP; + m_context.appendJump(eth::AssemblyItem::JumpType::OutOfFunction); return false; } diff --git a/libsolidity/CompilerContext.cpp b/libsolidity/CompilerContext.cpp index 1dea62e93..f2bb1de20 100644 --- a/libsolidity/CompilerContext.cpp +++ b/libsolidity/CompilerContext.cpp @@ -177,6 +177,13 @@ u256 CompilerContext::getStorageLocationOfVariable(const Declaration& _declarati return it->second; } +CompilerContext& CompilerContext::appendJump(eth::AssemblyItem::JumpType _jumpType) +{ + eth::AssemblyItem item(eth::Instruction::JUMP); + item.setJumpType(_jumpType); + return *this << item; +} + void CompilerContext::resetVisitedNodes(ASTNode const* _node) { stack newStack; diff --git a/libsolidity/CompilerContext.h b/libsolidity/CompilerContext.h index 4d63d8ba0..f468d29c4 100644 --- a/libsolidity/CompilerContext.h +++ b/libsolidity/CompilerContext.h @@ -91,7 +91,7 @@ public: /// Appends a JUMP to a new tag and @returns the tag eth::AssemblyItem appendJumpToNew() { return m_asm.appendJump().tag(); } /// Appends a JUMP to a tag already on the stack - CompilerContext& appendJump() { return *this << eth::Instruction::JUMP; } + CompilerContext& appendJump(eth::AssemblyItem::JumpType _jumpType = eth::AssemblyItem::JumpType::Ordinary); /// Appends a JUMP to a specific tag CompilerContext& appendJumpTo(eth::AssemblyItem const& _tag) { m_asm.appendJump(_tag); return *this; } /// Appends pushing of a new tag and @returns the new tag. diff --git a/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp index d2457e676..129261120 100644 --- a/libsolidity/ExpressionCompiler.cpp +++ b/libsolidity/ExpressionCompiler.cpp @@ -108,7 +108,8 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& retSizeOnStack = returnType->getSizeOnStack(); } solAssert(retSizeOnStack <= 15, "Stack too deep."); - m_context << eth::dupInstruction(retSizeOnStack + 1) << eth::Instruction::JUMP; + m_context << eth::dupInstruction(retSizeOnStack + 1); + m_context.appendJump(eth::AssemblyItem::JumpType::OutOfFunction); } void ExpressionCompiler::appendTypeConversion(Type const& _typeOnStack, Type const& _targetType, bool _cleanupNeeded) @@ -405,7 +406,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) } _functionCall.getExpression().accept(*this); - m_context.appendJump(); + m_context.appendJump(eth::AssemblyItem::JumpType::IntoFunction); m_context << returnLabel; unsigned returnParametersSize = CompilerUtils::getSizeOnStack(function.getReturnParameterTypes()); From 035451db40a9630915dd27b20be23d450c0a1cf1 Mon Sep 17 00:00:00 2001 From: Liana Husikyan Date: Tue, 10 Mar 2015 12:00:23 +0100 Subject: [PATCH 070/100] added brackets for printing in/out cleaned up --- libevmcore/Assembly.cpp | 10 +++++----- libevmcore/Assembly.h | 6 +++--- libsolidity/Compiler.cpp | 8 ++------ libsolidity/CompilerContext.h | 2 +- 4 files changed, 11 insertions(+), 15 deletions(-) diff --git a/libevmcore/Assembly.cpp b/libevmcore/Assembly.cpp index 57e124fb8..0a0611292 100644 --- a/libevmcore/Assembly.cpp +++ b/libevmcore/Assembly.cpp @@ -82,9 +82,9 @@ string AssemblyItem::getJumpTypeAsString() const switch (m_jumpType) { case JumpType::IntoFunction: - return "in"; + return "[in]"; case JumpType::OutOfFunction: - return "out"; + return "[out]"; case JumpType::Ordinary: default: return ""; @@ -210,7 +210,7 @@ string Assembly::getLocationFromSources(StringMap const& _sourceCodes, SourceLoc return move(cut); } -ostream& Assembly::streamRLP(ostream& _out, string const& _prefix, StringMap const& _sourceCodes) const +ostream& Assembly::stream(ostream& _out, string const& _prefix, StringMap const& _sourceCodes) const { _out << _prefix << ".code:" << endl; for (AssemblyItem const& i: m_items) @@ -219,7 +219,7 @@ ostream& Assembly::streamRLP(ostream& _out, string const& _prefix, StringMap con switch (i.m_type) { case Operation: - _out << " " << instructionInfo((Instruction)(byte)i.m_data).name << "\t\t" << i.getJumpTypeAsString(); + _out << " " << instructionInfo((Instruction)(byte)i.m_data).name << "\t" << i.getJumpTypeAsString(); break; case Push: _out << " PUSH " << i.m_data; @@ -266,7 +266,7 @@ ostream& Assembly::streamRLP(ostream& _out, string const& _prefix, StringMap con for (size_t i = 0; i < m_subs.size(); ++i) { _out << _prefix << " " << hex << i << ": " << endl; - m_subs[i].streamRLP(_out, _prefix + " ", _sourceCodes); + m_subs[i].stream(_out, _prefix + " ", _sourceCodes); } } return _out; diff --git a/libevmcore/Assembly.h b/libevmcore/Assembly.h index 8952deffd..1cbb12e12 100644 --- a/libevmcore/Assembly.h +++ b/libevmcore/Assembly.h @@ -121,7 +121,7 @@ public: void popTo(int _deposit) { while (m_deposit > _deposit) append(Instruction::POP); } void injectStart(AssemblyItem const& _i); - std::string out() const { std::stringstream ret; streamRLP(ret); return ret.str(); } + std::string out() const { std::stringstream ret; stream(ret); return ret.str(); } int deposit() const { return m_deposit; } void adjustDeposit(int _adjustment) { m_deposit += _adjustment; if (asserts(m_deposit >= 0)) BOOST_THROW_EXCEPTION(InvalidDeposit()); } void setDeposit(int _deposit) { m_deposit = _deposit; if (asserts(m_deposit >= 0)) BOOST_THROW_EXCEPTION(InvalidDeposit()); } @@ -131,7 +131,7 @@ public: bytes assemble() const; Assembly& optimise(bool _enable); - std::ostream& streamRLP(std::ostream& _out, std::string const& _prefix = "", const StringMap &_sourceCodes = StringMap()) const; + std::ostream& stream(std::ostream& _out, std::string const& _prefix = "", const StringMap &_sourceCodes = StringMap()) const; protected: std::string getLocationFromSources(StringMap const& _sourceCodes, SourceLocation const& _location) const; @@ -153,7 +153,7 @@ protected: inline std::ostream& operator<<(std::ostream& _out, Assembly const& _a) { - _a.streamRLP(_out); + _a.stream(_out); return _out; } diff --git a/libsolidity/Compiler.cpp b/libsolidity/Compiler.cpp index 46888683e..dc6e2c5a8 100644 --- a/libsolidity/Compiler.cpp +++ b/libsolidity/Compiler.cpp @@ -177,9 +177,7 @@ void Compiler::appendFunctionSelector(ContractDefinition const& _contract) { callDataUnpackerEntryPoints.insert(std::make_pair(it.first, m_context.newTag())); m_context << eth::dupInstruction(1) << u256(FixedHash<4>::Arith(it.first)) << eth::Instruction::EQ; - auto assemblyItem = callDataUnpackerEntryPoints.at(it.first); - //assemblyItem.setJumpType(eth::AssemblyItem::JumpType::IntoFunction); - m_context.appendConditionalJumpTo(assemblyItem); + m_context.appendConditionalJumpTo(callDataUnpackerEntryPoints.at(it.first)); } if (FunctionDefinition const* fallback = _contract.getFallbackFunction()) { @@ -199,9 +197,7 @@ void Compiler::appendFunctionSelector(ContractDefinition const& _contract) m_context << callDataUnpackerEntryPoints.at(it.first); eth::AssemblyItem returnTag = m_context.pushNewTag(); appendCalldataUnpacker(functionType->getParameterTypes()); - auto assemblyItem = m_context.getFunctionEntryLabel(functionType->getDeclaration()); - //assemblyItem.setJumpType(eth::AssemblyItem::JumpType::IntoFunction); - m_context.appendJumpTo(assemblyItem); + m_context.appendJumpTo(m_context.getFunctionEntryLabel(functionType->getDeclaration())); m_context << returnTag; appendReturnValuePacker(functionType->getReturnParameterTypes()); } diff --git a/libsolidity/CompilerContext.h b/libsolidity/CompilerContext.h index f468d29c4..76923a77a 100644 --- a/libsolidity/CompilerContext.h +++ b/libsolidity/CompilerContext.h @@ -120,7 +120,7 @@ public: eth::Assembly const& getAssembly() const { return m_asm; } /// @arg _sourceCodes is the map of input files to source code strings - void streamAssembly(std::ostream& _stream, StringMap const& _sourceCodes = StringMap()) const { m_asm.streamRLP(_stream, "", _sourceCodes); } + void streamAssembly(std::ostream& _stream, StringMap const& _sourceCodes = StringMap()) const { m_asm.stream(_stream, "", _sourceCodes); } bytes getAssembledBytecode(bool _optimize = false) { return m_asm.optimise(_optimize).assemble(); } From 68cd82a561cff29d5f58b773df955e75fc7de891 Mon Sep 17 00:00:00 2001 From: yann300 Date: Tue, 10 Mar 2015 12:24:40 +0100 Subject: [PATCH 071/100] QML Styling --- mix/qml/LogsPane.qml | 346 +++++++++++++++++++------------------- mix/qml/LogsPaneStyle.qml | 28 +++ mix/qml/StatusPane.qml | 6 +- mix/qml/qmldir | 1 + mix/res.qrc | 1 + 5 files changed, 206 insertions(+), 176 deletions(-) create mode 100644 mix/qml/LogsPaneStyle.qml diff --git a/mix/qml/LogsPane.qml b/mix/qml/LogsPane.qml index 67bdd44c1..bc192f1a7 100644 --- a/mix/qml/LogsPane.qml +++ b/mix/qml/LogsPane.qml @@ -15,9 +15,9 @@ Rectangle anchors.fill: parent radius: 5 - color: "#f7f7f7" - border.color: "#808080" - border.width: 1 + color: LogsPaneStyle.generic.layout.backgroundColor + border.color: LogsPaneStyle.generic.layout.borderColor + border.width: LogsPaneStyle.generic.layout.borderWidth ColumnLayout { z: 2 height: parent.height @@ -26,14 +26,14 @@ Rectangle Row { id: rowAction - Layout.preferredHeight: 35 - height: 35 - anchors.leftMargin: 10 + Layout.preferredHeight: LogsPaneStyle.generic.layout.headerHeight + height: LogsPaneStyle.generic.layout.headerHeight + anchors.leftMargin: LogsPaneStyle.generic.layout.leftMargin anchors.left: parent.left - spacing: 5 + spacing: LogsPaneStyle.generic.layout.headerButtonSpacing Button { - height: 30 + height: LogsPaneStyle.generic.layout.headerButtonHeight anchors.verticalCenter: parent.verticalCenter action: clearAction iconSource: "qrc:/qml/img/broom.png" @@ -50,7 +50,7 @@ Rectangle Button { - height: 30 + height: LogsPaneStyle.generic.layout.headerButtonHeight anchors.verticalCenter: parent.verticalCenter action: copytoClipBoardAction iconSource: "qrc:/qml/img/copy.png" @@ -81,7 +81,7 @@ Rectangle ToolButton { id: javascriptButton checkable: true - height: 30 + height: LogsPaneStyle.generic.layout.headerButtonHeight anchors.verticalCenter: parent.verticalCenter checked: true onCheckedChanged: { @@ -89,212 +89,212 @@ Rectangle } tooltip: qsTr("JavaScript") style: - ButtonStyle { + ButtonStyle { label: Item { DefaultLabel { - font.family: "sans serif" + font.family: LogsPaneStyle.generic.layout.logLabelFont font.pointSize: Style.absoluteSize(-3) - color: "#5391d8" + color: LogsPaneStyle.generic.layout.logLabelColor anchors.centerIn: parent text: qsTr("JavaScript") } } + } } - } - ToolButton { - id: runButton - checkable: true - height: 30 - anchors.verticalCenter: parent.verticalCenter - checked: true - onCheckedChanged: { - proxyModel.toogleFilter("run") - } - tooltip: qsTr("Run") - style: - ButtonStyle { - label: - Item { - DefaultLabel { - font.family: "sans serif" - font.pointSize: Style.absoluteSize(-3) - color: "#5391d8" - anchors.centerIn: parent - text: qsTr("Run") + ToolButton { + id: runButton + checkable: true + height: LogsPaneStyle.generic.layout.headerButtonHeight + anchors.verticalCenter: parent.verticalCenter + checked: true + onCheckedChanged: { + proxyModel.toogleFilter("run") + } + tooltip: qsTr("Run") + style: + ButtonStyle { + label: + Item { + DefaultLabel { + font.family: LogsPaneStyle.generic.layout.logLabelFont + font.pointSize: Style.absoluteSize(-3) + color: LogsPaneStyle.generic.layout.logLabelColor + anchors.centerIn: parent + text: qsTr("Run") + } } } } - } - ToolButton { - id: stateButton - checkable: true - height: 30 - anchors.verticalCenter: parent.verticalCenter - checked: true - onCheckedChanged: { - proxyModel.toogleFilter("state") - } - tooltip: qsTr("State") - style: - ButtonStyle { - label: - Item { - DefaultLabel { - font.family: "sans serif" - font.pointSize: Style.absoluteSize(-3) - color: "#5391d8" - anchors.centerIn: parent - text: qsTr("State") + ToolButton { + id: stateButton + checkable: true + height: LogsPaneStyle.generic.layout.headerButtonHeight + anchors.verticalCenter: parent.verticalCenter + checked: true + onCheckedChanged: { + proxyModel.toogleFilter("state") + } + tooltip: qsTr("State") + style: + ButtonStyle { + label: + Item { + DefaultLabel { + font.family: LogsPaneStyle.generic.layout.logLabelFont + font.pointSize: Style.absoluteSize(-3) + color: "#5391d8" + anchors.centerIn: parent + text: qsTr("State") + } } } } - } - ToolButton { - id: compilationButton - checkable: true - height: 30 - anchors.verticalCenter: parent.verticalCenter - checked: false - onCheckedChanged: { - proxyModel.toogleFilter("compilation") - } - tooltip: qsTr("Compilation") - style: - ButtonStyle { - label: - Item { - DefaultLabel { - font.family: "sans serif" - font.pointSize: Style.absoluteSize(-3) - color: "#5391d8" - anchors.centerIn: parent - text: qsTr("Compilation") + ToolButton { + id: compilationButton + checkable: true + height: LogsPaneStyle.generic.layout.headerButtonHeight + anchors.verticalCenter: parent.verticalCenter + checked: false + onCheckedChanged: { + proxyModel.toogleFilter("compilation") + } + tooltip: qsTr("Compilation") + style: + ButtonStyle { + label: + Item { + DefaultLabel { + font.family: LogsPaneStyle.generic.layout.logLabelFont + font.pointSize: Style.absoluteSize(-3) + color: "#5391d8" + anchors.centerIn: parent + text: qsTr("Compilation") + } } } } - } - DefaultTextField - { - id: searchBox - height: 30 - anchors.verticalCenter: parent.verticalCenter - width: 200 - font.family: "sans serif" - font.pointSize: Style.absoluteSize(-3) - font.italic: true - onTextChanged: { - proxyModel.search(text); + DefaultTextField + { + id: searchBox + height: LogsPaneStyle.generic.layout.headerButtonHeight + anchors.verticalCenter: parent.verticalCenter + width: LogsPaneStyle.generic.layout.headerInputWidth + font.family: LogsPaneStyle.generic.layout.logLabelFont + font.pointSize: Style.absoluteSize(-3) + font.italic: true + onTextChanged: { + proxyModel.search(text); + } } } - } - ListModel { - id: logsModel - } - - TableView { - id: logsTable - clip: true - Layout.fillWidth: true - Layout.preferredHeight: parent.height - rowAction.height - headerVisible : false - onDoubleClicked: - { - var log = logsModel.get((logsTable.currentRow)); - if (log) - appContext.toClipboard(log.type + "\t" + log.level + "\t" + log.date + "\t" + log.content); + ListModel { + id: logsModel } - model: SortFilterProxyModel { - id: proxyModel - source: logsModel - property var roles: ["-", "javascript", "run", "state"] - - Component.onCompleted: { - filterType = regEx(proxyModel.roles); - } - - function search(_value) + TableView { + id: logsTable + clip: true + Layout.fillWidth: true + Layout.preferredHeight: parent.height - rowAction.height + headerVisible : false + onDoubleClicked: { - filterContent = _value; + var log = logsModel.get((logsTable.currentRow)); + if (log) + appContext.toClipboard(log.type + "\t" + log.level + "\t" + log.date + "\t" + log.content); } - function toogleFilter(_value) - { - var count = roles.length; - for (var i in roles) + model: SortFilterProxyModel { + id: proxyModel + source: logsModel + property var roles: ["-", "javascript", "run", "state"] + + Component.onCompleted: { + filterType = regEx(proxyModel.roles); + } + + function search(_value) { - if (roles[i] === _value) + filterContent = _value; + } + + function toogleFilter(_value) + { + var count = roles.length; + for (var i in roles) { - roles.splice(i, 1); - break; + if (roles[i] === _value) + { + roles.splice(i, 1); + break; + } } + if (count === roles.length) + roles.push(_value); + + filterType = regEx(proxyModel.roles); } - if (count === roles.length) - roles.push(_value); - filterType = regEx(proxyModel.roles); - } + function regEx(_value) + { + return "(?:" + roles.join('|') + ")"; + } - function regEx(_value) + filterType: "(?:javascript|run|state)" + filterContent: "" + filterSyntax: SortFilterProxyModel.RegExp + filterCaseSensitivity: Qt.CaseInsensitive + } + TableViewColumn { - return "(?:" + roles.join('|') + ")"; + role: "date" + title: qsTr("date") + width: LogsPaneStyle.generic.layout.dateWidth + delegate: itemDelegate + } + TableViewColumn + { + role: "type" + title: qsTr("type") + width: LogsPaneStyle.generic.layout.typeWidth + delegate: itemDelegate + } + TableViewColumn + { + role: "content" + title: qsTr("content") + width: LogsPaneStyle.generic.layout.contentWidth + delegate: itemDelegate } - filterType: "(?:javascript|run|state)" - filterContent: "" - filterSyntax: SortFilterProxyModel.RegExp - filterCaseSensitivity: Qt.CaseInsensitive - } - TableViewColumn - { - role: "date" - title: qsTr("date") - width: 150 - delegate: itemDelegate - } - TableViewColumn - { - role: "type" - title: qsTr("type") - width: 80 - delegate: itemDelegate - } - TableViewColumn - { - role: "content" - title: qsTr("content") - width: 700 - delegate: itemDelegate - } - - rowDelegate: Item { - Rectangle { - width: logsTable.width - 4 - height: 17 - color: styleData.alternate ? "transparent" : "#f0f0f0" + rowDelegate: Item { + Rectangle { + width: logsTable.width - 4 + height: 17 + color: styleData.alternate ? "transparent" : "#f0f0f0" + } } } - } - Component { - id: itemDelegate - DefaultLabel { - text: styleData.value; - font.family: "sans serif" - font.pointSize: Style.absoluteSize(-1) - color: { - if (proxyModel.get(styleData.row).level === "error") - return "red" - else - return "#808080" + Component { + id: itemDelegate + DefaultLabel { + text: styleData.value; + font.family: LogsPaneStyle.generic.layout.logLabelFont + font.pointSize: Style.absoluteSize(-1) + color: { + if (proxyModel.get(styleData.row).level === "error") + return "red" + else + return "#808080" + } } } } } -} diff --git a/mix/qml/LogsPaneStyle.qml b/mix/qml/LogsPaneStyle.qml new file mode 100644 index 000000000..c47ee6338 --- /dev/null +++ b/mix/qml/LogsPaneStyle.qml @@ -0,0 +1,28 @@ +pragma Singleton +import QtQuick 2.0 + +QtObject { + + function absoluteSize(rel) + { + return systemPointSize + rel; + } + + property QtObject generic: QtObject { + property QtObject layout: QtObject { + property string backgroundColor: "#f7f7f7" + property string borderColor: "#5391d8" + property int borderWidth: 1 + property int headerHeight: 35 + property int headerButtonSpacing: 5 + property int leftMargin: 10 + property int headerButtonHeight: 30 + property string logLabelColor: "#5391d8" + property string logLabelFont: "sans serif" + property int headerInputWidth: 200 + property int dateWidth: 150 + property int typeWidth: 80 + property int contentWidth: 700 + } + } +} diff --git a/mix/qml/StatusPane.qml b/mix/qml/StatusPane.qml index 1ce7917a9..e9ff312a9 100644 --- a/mix/qml/StatusPane.qml +++ b/mix/qml/StatusPane.qml @@ -116,7 +116,7 @@ Rectangle { name: "logsOpened" PropertyChanges { target: statusContainer - border.color: "#808080" + border.color: "#5391d8" border.width: 1 } }, @@ -124,7 +124,7 @@ Rectangle { name: "logsClosed" PropertyChanges { target: statusContainer - border.color: "#808080" + border.color: "#5391d8" border.width: 0 } } @@ -206,7 +206,7 @@ Rectangle { } else { - statusContainer.state = "'logsOpened"; + statusContainer.state = "logsOpened"; logsContainer.state = "opened"; logsContainer.focus = true; forceActiveFocus(); diff --git a/mix/qml/qmldir b/mix/qml/qmldir index 0f3fcd88c..73c117941 100644 --- a/mix/qml/qmldir +++ b/mix/qml/qmldir @@ -5,3 +5,4 @@ singleton DebuggerPaneStyle 1.0 DebuggerPaneStyle.qml singleton StateStyle 1.0 StateStyle.qml singleton StatusPaneStyle 1.0 StatusPaneStyle.qml singleton WebPreviewStyle 1.0 WebPreviewStyle.qml +singleton LogsPaneStyle 1.0 LogsPaneStyle.qml diff --git a/mix/res.qrc b/mix/res.qrc index 8bda8124c..e953b3e35 100644 --- a/mix/res.qrc +++ b/mix/res.qrc @@ -114,5 +114,6 @@ qml/LogsPane.qml qml/img/copy.png qml/img/broom.png + qml/LogsPaneStyle.qml From 47d7f571c3dc8342e044a876d577e3702a8ff06a Mon Sep 17 00:00:00 2001 From: yann300 Date: Tue, 10 Mar 2015 12:34:29 +0100 Subject: [PATCH 072/100] add warning level --- mix/qml/LogsPane.qml | 6 ++++-- mix/qml/StatusPane.qml | 13 ++++++++++++- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/mix/qml/LogsPane.qml b/mix/qml/LogsPane.qml index bc192f1a7..8956abdf2 100644 --- a/mix/qml/LogsPane.qml +++ b/mix/qml/LogsPane.qml @@ -290,9 +290,11 @@ Rectangle font.pointSize: Style.absoluteSize(-1) color: { if (proxyModel.get(styleData.row).level === "error") - return "red" + return "red"; + else if (proxyModel.get(styleData.row).level === "warning") + return "orange"; else - return "#808080" + return "#808080"; } } } diff --git a/mix/qml/StatusPane.qml b/mix/qml/StatusPane.qml index e9ff312a9..71f622865 100644 --- a/mix/qml/StatusPane.qml +++ b/mix/qml/StatusPane.qml @@ -38,7 +38,7 @@ Rectangle { function warningMessage(text, type) { - status.state = ""; + status.state = "warning"; status.text = text logPane.push("warning", type, text); } @@ -153,6 +153,17 @@ Rectangle { target: statusContainer color: "#fffcd5" } + }, + State { + name: "warning" + PropertyChanges { + target: status + color: "orange" + } + PropertyChanges { + target: statusContainer + color: "#fffcd5" + } } ] onTextChanged: From fac9e35dfbc3015b3f4a7664a97861f1dc40edf6 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Tue, 10 Mar 2015 12:37:30 +0100 Subject: [PATCH 073/100] Remove StructuredLogger dependency on libp2p --- libdevcore/StructuredLogger.cpp | 3 ++- libdevcore/StructuredLogger.h | 11 ++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/libdevcore/StructuredLogger.cpp b/libdevcore/StructuredLogger.cpp index d93a9496e..ff267ff4e 100644 --- a/libdevcore/StructuredLogger.cpp +++ b/libdevcore/StructuredLogger.cpp @@ -22,11 +22,12 @@ */ #include "StructuredLogger.h" - #include +#include #include #include "Guards.h" +namespace ba = boost::asio; using namespace std; namespace dev diff --git a/libdevcore/StructuredLogger.h b/libdevcore/StructuredLogger.h index 58b408ede..eea19d107 100644 --- a/libdevcore/StructuredLogger.h +++ b/libdevcore/StructuredLogger.h @@ -27,9 +27,10 @@ #include #include -#include namespace Json { class Value; } +namespace boost { namespace asio { namespace ip { templateclass basic_endpoint; class tcp; }}} +namespace bi = boost::asio::ip; namespace dev { @@ -61,12 +62,16 @@ public: static void stopping(std::string const& _clientImpl, const char* _ethVersion); static void p2pConnected( std::string const& _id, - bi::tcp::endpoint const& _addr, + bi::basic_endpoint const& _addr, std::chrono::system_clock::time_point const& _ts, std::string const& _remoteVersion, unsigned int _numConnections ); - static void p2pDisconnected(std::string const& _id, bi::tcp::endpoint const& _addr, unsigned int _numConnections); + static void p2pDisconnected( + std::string const& _id, + bi::basic_endpoint const& _addr, + unsigned int _numConnections + ); static void minedNewBlock( std::string const& _hash, std::string const& _blockNumber, From 0d444716b012a541caf9c7f13076b0894b8cb2ee Mon Sep 17 00:00:00 2001 From: yann300 Date: Tue, 10 Mar 2015 12:38:55 +0100 Subject: [PATCH 074/100] small changes --- mix/qml/LogsPane.qml | 2 +- mix/qml/LogsPaneStyle.qml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/mix/qml/LogsPane.qml b/mix/qml/LogsPane.qml index 8956abdf2..9d03b2af2 100644 --- a/mix/qml/LogsPane.qml +++ b/mix/qml/LogsPane.qml @@ -277,7 +277,7 @@ Rectangle Rectangle { width: logsTable.width - 4 height: 17 - color: styleData.alternate ? "transparent" : "#f0f0f0" + color: styleData.alternate ? "transparent" : LogsPaneStyle.generic.layout.logAlternateColor } } } diff --git a/mix/qml/LogsPaneStyle.qml b/mix/qml/LogsPaneStyle.qml index c47ee6338..59b80653a 100644 --- a/mix/qml/LogsPaneStyle.qml +++ b/mix/qml/LogsPaneStyle.qml @@ -23,6 +23,7 @@ QtObject { property int dateWidth: 150 property int typeWidth: 80 property int contentWidth: 700 + property string logAlternateColor: "#f0f0f0" } } } From b0b1c5bca8592147099a458c0c56e92eefb89457 Mon Sep 17 00:00:00 2001 From: yann300 Date: Tue, 10 Mar 2015 12:41:31 +0100 Subject: [PATCH 075/100] small changes --- mix/qml/StatusPane.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mix/qml/StatusPane.qml b/mix/qml/StatusPane.qml index 71f622865..20b30ce3b 100644 --- a/mix/qml/StatusPane.qml +++ b/mix/qml/StatusPane.qml @@ -33,7 +33,7 @@ Rectangle { { status.state = ""; status.text = text - logPane.push("info",type, text); + logPane.push("info", type, text); } function warningMessage(text, type) @@ -61,7 +61,7 @@ Rectangle { var message = _sourceId.substring(_sourceId.lastIndexOf("/") + 1) + " - " + qsTr("line") + " " + _lineNb + " - " + _content; if (_level === 1) warningMessage(message, "JavaScript") - else if (_level === 2) + else errorMessage(message, "JavaScript") } } From 071f81652b5204bccc6fd3dd775689fa6ddb38ca Mon Sep 17 00:00:00 2001 From: yann300 Date: Tue, 10 Mar 2015 14:28:27 +0100 Subject: [PATCH 076/100] Replace Qt header by Ethereum header. --- mix/AppContext.cpp | 2 +- ...roxymodel.cpp => Sortfilterproxymodel.cpp} | 65 ++++------ mix/Sortfilterproxymodel.h | 97 +++++++++++++++ mix/sortfilterproxymodel.h | 114 ------------------ 4 files changed, 122 insertions(+), 156 deletions(-) rename mix/{sortfilterproxymodel.cpp => Sortfilterproxymodel.cpp} (63%) create mode 100644 mix/Sortfilterproxymodel.h delete mode 100644 mix/sortfilterproxymodel.h diff --git a/mix/AppContext.cpp b/mix/AppContext.cpp index 46bb0f98d..b649ee4c2 100644 --- a/mix/AppContext.cpp +++ b/mix/AppContext.cpp @@ -38,7 +38,7 @@ #include "QVariableDefinition.h" #include "HttpServer.h" #include "AppContext.h" -#include "sortfilterproxymodel.h" +#include "Sortfilterproxymodel.h" using namespace dev; using namespace dev::eth; diff --git a/mix/sortfilterproxymodel.cpp b/mix/Sortfilterproxymodel.cpp similarity index 63% rename from mix/sortfilterproxymodel.cpp rename to mix/Sortfilterproxymodel.cpp index 62d85cfa3..64689842b 100644 --- a/mix/sortfilterproxymodel.cpp +++ b/mix/Sortfilterproxymodel.cpp @@ -1,44 +1,27 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the examples of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names -** of its contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "sortfilterproxymodel.h" +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** + * @author Yann + * @date 2015 + * Proxy used to filter a QML TableView. + */ + + +#include "Sortfilterproxymodel.h" #include #include diff --git a/mix/Sortfilterproxymodel.h b/mix/Sortfilterproxymodel.h new file mode 100644 index 000000000..de9a2005f --- /dev/null +++ b/mix/Sortfilterproxymodel.h @@ -0,0 +1,97 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** + * @author Yann + * @date 2015 + * Proxy used to filter a QML TableView. + */ + + +#pragma once + +#include +#include + +namespace dev +{ +namespace mix +{ + +class SortFilterProxyModel: public QSortFilterProxyModel +{ + Q_OBJECT + Q_PROPERTY(int count READ count NOTIFY countChanged) + Q_PROPERTY(QObject* source READ source WRITE setSource) + + Q_PROPERTY(QByteArray sortRole READ sortRole WRITE setSortRole) + Q_PROPERTY(Qt::SortOrder sortOrder READ sortOrder WRITE setSortOrder) + + Q_PROPERTY(QString filterContent READ filterContent WRITE setFilterContent) + Q_PROPERTY(QString filterType READ filterType WRITE setFilterType) + Q_PROPERTY(QString filterString READ filterString WRITE setFilterString) + Q_PROPERTY(FilterSyntax filterSyntax READ filterSyntax WRITE setFilterSyntax) + + Q_ENUMS(FilterSyntax) + +public: + explicit SortFilterProxyModel(QObject* _parent = 0); + + QObject* source() const; + void setSource(QObject* _source); + + QByteArray sortRole() const; + void setSortRole(QByteArray const& _role); + + void setSortOrder(Qt::SortOrder _order); + + QString filterContent() const; + void setFilterContent(QString const& _content); + QString filterType() const; + void setFilterType(QString const& _type); + + QString filterString() const; + void setFilterString(QString const& _filter); + + enum FilterSyntax { + RegExp, + Wildcard, + FixedString + }; + + FilterSyntax filterSyntax() const; + void setFilterSyntax(FilterSyntax _syntax); + + int count() const; + Q_INVOKABLE QJSValue get(int _index) const; + +signals: + void countChanged(); + +protected: + int roleKey(QByteArray const& _role) const; + QHash roleNames() const; + bool filterAcceptsRow(int _sourceRow, QModelIndex const& _sourceParent) const; + +private: + QRegExp m_filterType; + QRegExp m_filterContent; + const QString type = "type"; + const QString content = "content"; +}; + +} +} diff --git a/mix/sortfilterproxymodel.h b/mix/sortfilterproxymodel.h deleted file mode 100644 index bc220a911..000000000 --- a/mix/sortfilterproxymodel.h +++ /dev/null @@ -1,114 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the examples of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names -** of its contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#pragma once - -#include -#include - -namespace dev -{ -namespace mix -{ - -class SortFilterProxyModel: public QSortFilterProxyModel -{ - Q_OBJECT - Q_PROPERTY(int count READ count NOTIFY countChanged) - Q_PROPERTY(QObject* source READ source WRITE setSource) - - Q_PROPERTY(QByteArray sortRole READ sortRole WRITE setSortRole) - Q_PROPERTY(Qt::SortOrder sortOrder READ sortOrder WRITE setSortOrder) - - Q_PROPERTY(QString filterContent READ filterContent WRITE setFilterContent) - Q_PROPERTY(QString filterType READ filterType WRITE setFilterType) - Q_PROPERTY(QString filterString READ filterString WRITE setFilterString) - Q_PROPERTY(FilterSyntax filterSyntax READ filterSyntax WRITE setFilterSyntax) - - Q_ENUMS(FilterSyntax) - -public: - explicit SortFilterProxyModel(QObject* _parent = 0); - - QObject* source() const; - void setSource(QObject* _source); - - QByteArray sortRole() const; - void setSortRole(QByteArray const& _role); - - void setSortOrder(Qt::SortOrder _order); - - QString filterContent() const; - void setFilterContent(QString const& _content); - QString filterType() const; - void setFilterType(QString const& _type); - - QString filterString() const; - void setFilterString(QString const& _filter); - - enum FilterSyntax { - RegExp, - Wildcard, - FixedString - }; - - FilterSyntax filterSyntax() const; - void setFilterSyntax(FilterSyntax _syntax); - - int count() const; - Q_INVOKABLE QJSValue get(int _index) const; - -signals: - void countChanged(); - -protected: - int roleKey(QByteArray const& _role) const; - QHash roleNames() const; - bool filterAcceptsRow(int _sourceRow, QModelIndex const& _sourceParent) const; - -private: - QRegExp m_filterType; - QRegExp m_filterContent; - const QString type = "type"; - const QString content = "content"; -}; - -} -} From ea5bfadcd81ca9f6dc699b77c7c913a2554ea638 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 10 Mar 2015 15:11:13 +0100 Subject: [PATCH 077/100] Fixed some checker warnings. --- libsolidity/Compiler.h | 4 ++-- libsolidity/Parser.h | 2 ++ solc/CommandLineInterface.cpp | 7 ------- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/libsolidity/Compiler.h b/libsolidity/Compiler.h index 76f16f3ab..4b1e1b4d6 100644 --- a/libsolidity/Compiler.h +++ b/libsolidity/Compiler.h @@ -94,8 +94,8 @@ private: std::vector m_continueTags; ///< tag to jump to for a "continue" statement eth::AssemblyItem m_returnTag; ///< tag to jump to for a "return" statement unsigned m_modifierDepth = 0; - FunctionDefinition const* m_currentFunction; - unsigned m_stackCleanupForReturn; ///< this number of stack elements need to be removed before jump to m_returnTag + FunctionDefinition const* m_currentFunction = nullptr; + unsigned m_stackCleanupForReturn = 0; ///< this number of stack elements need to be removed before jump to m_returnTag // arguments for base constructors, filled in derived-to-base order std::map> const*> m_baseArguments; }; diff --git a/libsolidity/Parser.h b/libsolidity/Parser.h index 87eb2f8ff..cc0b2ca17 100644 --- a/libsolidity/Parser.h +++ b/libsolidity/Parser.h @@ -34,6 +34,8 @@ class Scanner; class Parser { public: + Parser() {} + ASTPointer parse(std::shared_ptr const& _scanner); std::shared_ptr const& getSourceName() const; diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 08acb5ebd..867e832d7 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -345,18 +345,11 @@ bool CommandLineInterface::processInput() void CommandLineInterface::handleAst(string const& _argStr) { string title; - string suffix; if (_argStr == g_argAstStr) - { title = "Syntax trees:"; - suffix = ".ast"; - } else if (_argStr == g_argAstJson) - { title = "JSON AST:"; - suffix = ".json"; - } else BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Illegal argStr for AST")); From 91d453db92867f2378c7d2e9a2e0f8ef8e9f1b5a Mon Sep 17 00:00:00 2001 From: arkpar Date: Fri, 20 Feb 2015 22:36:23 +0100 Subject: [PATCH 078/100] DApp load in AZ --- alethzero/CMakeLists.txt | 1 + alethzero/DappHost.cpp | 141 ++++++++++++++++++++++++++ alethzero/DappHost.h | 58 +++++++++++ alethzero/DappLoader.cpp | 203 +++++++++++++++++++++++++++++++++++++ alethzero/DappLoader.h | 94 +++++++++++++++++ alethzero/MainWin.cpp | 71 +++++++++---- alethzero/MainWin.h | 14 ++- alethzero/Transact.cpp | 4 +- alethzero/WebPage.cpp | 29 ++++++ alethzero/WebPage.h | 37 +++++++ mix/FileIo.cpp | 2 +- mix/qml/js/ProjectModel.js | 20 ++-- 12 files changed, 636 insertions(+), 38 deletions(-) create mode 100644 alethzero/DappHost.cpp create mode 100644 alethzero/DappHost.h create mode 100644 alethzero/DappLoader.cpp create mode 100644 alethzero/DappLoader.h create mode 100644 alethzero/WebPage.cpp create mode 100644 alethzero/WebPage.h diff --git a/alethzero/CMakeLists.txt b/alethzero/CMakeLists.txt index cbc9e087a..49c847a16 100644 --- a/alethzero/CMakeLists.txt +++ b/alethzero/CMakeLists.txt @@ -56,6 +56,7 @@ target_link_libraries(${EXECUTABLE} devcore) target_link_libraries(${EXECUTABLE} web3jsonrpc) target_link_libraries(${EXECUTABLE} jsqrc) target_link_libraries(${EXECUTABLE} natspec) +target_link_libraries(${EXECUTABLE} ${MHD_LIBRARIES}) if (NOT ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")) target_link_libraries(${EXECUTABLE} serpent) diff --git a/alethzero/DappHost.cpp b/alethzero/DappHost.cpp new file mode 100644 index 000000000..22ba41c6c --- /dev/null +++ b/alethzero/DappHost.cpp @@ -0,0 +1,141 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** @file DappHost.cpp + * @author Arkadiy Paronyan + * @date 2015 + */ + +#include +#include +#include +#include +#include "DappHost.h" + +using namespace dev; + +DappHost::DappHost(int _port, int _threads): + m_port(_port), m_threads(_threads), m_running(false), m_daemon(nullptr) +{ + startListening(); +} + +DappHost::~DappHost() +{ + stopListening(); +} + +void DappHost::startListening() +{ + if(!this->m_running) + { + this->m_daemon = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, this->m_port, nullptr, nullptr, &DappHost::callback, this, MHD_OPTION_THREAD_POOL_SIZE, this->m_threads, MHD_OPTION_END); + if (this->m_daemon != nullptr) + this->m_running = true; + } +} + +void DappHost::stopListening() +{ + if(this->m_running) + { + MHD_stop_daemon(this->m_daemon); + this->m_running = false; + } +} + +void DappHost::sendOptionsResponse(MHD_Connection* _connection) +{ + MHD_Response *result = MHD_create_response_from_data(0, NULL, 0, 1); + MHD_add_response_header(result, "Allow", "GET, OPTIONS"); + //MHD_add_response_header(result, "Access-Control-Allow-Origin", "*"); + MHD_add_response_header(result, "Access-Control-Allow-Headers", "origin, content-type, accept"); + MHD_add_response_header(result, "DAV", "1"); + MHD_queue_response(_connection, MHD_HTTP_OK, result); + MHD_destroy_response(result); +} + +void DappHost::sendNotAllowedResponse(MHD_Connection* _connection) +{ + MHD_Response *result = MHD_create_response_from_data(0, NULL, 0, 1); + MHD_add_response_header(result, "Allow", "GET, OPTIONS"); + MHD_queue_response(_connection, MHD_HTTP_METHOD_NOT_ALLOWED, result); + MHD_destroy_response(result); +} + +void DappHost::sendResponse(std::string const& _url, MHD_Connection* _connection) +{ + QUrl requestUrl(QString::fromStdString(_url)); + QString path = requestUrl.path().toLower(); + if (path.isEmpty()) + path = "/"; + + bytesConstRef response; + unsigned code = MHD_HTTP_NOT_FOUND; + std::string contentType; + + while (!path.isEmpty()) + { + auto iter = m_entriesByPath.find(path); + if (iter != m_entriesByPath.end()) + { + ManifestEntry const* entry = iter->second; + auto contentIter = m_dapp.content.find(entry->hash); + if (contentIter == m_dapp.content.end()) + break; + + response = bytesConstRef(contentIter->second.data(), contentIter->second.size()); + code = entry->httpStatus != 0 ? entry->httpStatus : MHD_HTTP_OK; + contentType = entry->contentType; + break; + } + path.truncate(path.length() - 1); + path = path.mid(0, path.lastIndexOf('/')); + } + + MHD_Response *result = MHD_create_response_from_data(response.size(), const_cast(response.data()), 0, 1); + if (!contentType.empty()) + MHD_add_response_header(result, "Content-Type", contentType.c_str()); + //MHD_add_response_header(result, "Access-Control-Allow-Origin", "*"); + MHD_queue_response(_connection, code, result); + MHD_destroy_response(result); +} + +int DappHost::callback(void* _cls, MHD_Connection* _connection, char const* _url, char const* _method, char const* _version, char const* _uploadData, size_t* _uploadDataSize, void** _conCls) +{ + (void)_version; + (void)_uploadData; + (void)_uploadDataSize; + (void)_conCls; + DappHost* host = static_cast(_cls); + if (std::string("GET") == _method) + host->sendResponse(std::string(_url), _connection); + else if (std::string("OPTIONS") == _method) + host->sendOptionsResponse(_connection); + else + host->sendNotAllowedResponse(_connection); + return MHD_YES; +} + +QUrl DappHost::hostDapp(Dapp&& _dapp) +{ + m_dapp = std::move(_dapp); + m_entriesByPath.clear(); + for (ManifestEntry const& entry: m_dapp.manifest.entries) + m_entriesByPath[QString::fromStdString(entry.path)] = &entry; + + return QUrl(QString("http://localhost:%1/").arg(m_port)); +} diff --git a/alethzero/DappHost.h b/alethzero/DappHost.h new file mode 100644 index 000000000..985bd34d9 --- /dev/null +++ b/alethzero/DappHost.h @@ -0,0 +1,58 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** @file DappHost.h + * @author Arkadiy Paronyan + * @date 2015 + */ + +#pragma once + +#include +#include +#include +#include "DappLoader.h" + +struct MHD_Daemon; +struct MHD_Connection; + +/// DApp web server. Servers web content, resolves paths by hashes +class DappHost +{ +public: + /// @param _port Network pork to listen for incoming connections + /// @param _threads Max number of threads to process requests + DappHost(int _port, int _threads = 10); + virtual ~DappHost(); + /// Load and host a dapp. Previsous dapp in discarded. Synchronous + QUrl hostDapp(Dapp&& _dapp); + +private: + void startListening(); + void stopListening(); + void sendOptionsResponse(MHD_Connection* _connection); + void sendNotAllowedResponse(MHD_Connection* _connection); + void sendResponse(std::string const& _url, MHD_Connection* _connection); + static int callback(void* _cls, MHD_Connection* _connection, char const* _url, char const* _method, char const* _version, char const* _uploadData, size_t* _uploadDataSize, void** _conCls); + + int m_port; + int m_threads; + bool m_running; + MHD_Daemon* m_daemon; + Dapp m_dapp; + std::map m_entriesByPath; +}; + diff --git a/alethzero/DappLoader.cpp b/alethzero/DappLoader.cpp new file mode 100644 index 000000000..41ac22b23 --- /dev/null +++ b/alethzero/DappLoader.cpp @@ -0,0 +1,203 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** @file DappLoader.cpp + * @author Arkadiy Paronyan + * @date 2015 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "DappLoader.h" + +using namespace dev; +using namespace dev::eth; +using namespace dev::crypto; + +Address c_registrar = Address("0000000000000000000000000000000000000a28"); +Address c_urlHint = Address("0000000000000000000000000000000000000a29"); + +QString contentsOfQResource(std::string const& res); + +DappLoader::DappLoader(QObject* _parent, WebThreeDirect* _web3): + QObject(_parent), m_web3(_web3) +{ + connect(&m_net, &QNetworkAccessManager::finished, this, &DappLoader::downloadComplete); +} + +DappLocation DappLoader::resolveAppUri(QString const& _uri) +{ + QUrl url(_uri); + if (!url.scheme().isEmpty() && url.scheme() != "eth") + throw dev::Exception(); //TODO: + + QStringList parts = url.host().split('.', QString::SkipEmptyParts); + QStringList domainParts; + std::reverse(parts.begin(), parts.end()); + parts.append(url.path().split('/', QString::SkipEmptyParts)); + + Address address = c_registrar; + Address lastAddress; + int partIndex = 0; + + h256 contentHash; + + while (address && partIndex < parts.length()) + { + lastAddress = address; + string32 name = { 0 }; + QByteArray utf8 = parts[partIndex].toUtf8(); + std::copy(utf8.data(), utf8.data() + utf8.size(), name.data()); + address = abiOut
(web3()->ethereum()->call(address, abiIn("addr(string32)", name))); + domainParts.append(parts[partIndex]); + if (!address) + { + //we have the address of the last part, try to get content hash + contentHash = abiOut(web3()->ethereum()->call(lastAddress, abiIn("content(string32)", name))); + if (!contentHash) + throw dev::Exception() << errinfo_comment("Can't resolve address"); + } + ++partIndex; + } + + + string32 contentUrl = abiOut(web3()->ethereum()->call(c_urlHint, abiIn("url(hash256)", contentHash))); + QString domain = domainParts.join('/'); + parts.erase(parts.begin(), parts.begin() + partIndex); + QString path = parts.join('/'); + QString contentUrlString = QString::fromUtf8(std::string(contentUrl.data(), contentUrl.size()).c_str()); + if (!contentUrlString.startsWith("http://") || !contentUrlString.startsWith("https://")) + contentUrlString = "http://" + contentUrlString; + return DappLocation { domain, path, contentUrlString, contentHash }; +} + +void DappLoader::downloadComplete(QNetworkReply* _reply) +{ + try + { + //try to interpret as rlp + QByteArray data = _reply->readAll(); + _reply->deleteLater(); + + h256 expected = m_uriHashes[_reply->request().url()]; + bytes package(reinterpret_cast(data.constData()), reinterpret_cast(data.constData() + data.size())); + Secp256k1 dec; + dec.decrypt(expected, package); + h256 got = sha3(package); + if (got != expected) + { + //try base64 + data = QByteArray::fromBase64(data); + package = bytes(reinterpret_cast(data.constData()), reinterpret_cast(data.constData() + data.size())); + dec.decrypt(expected, package); + got = sha3(package); + if (got != expected) + throw dev::Exception() << errinfo_comment("Dapp content hash does not match"); + } + + RLP rlp(package); + loadDapp(rlp); + } + catch (...) + { + qWarning() << tr("Error downloading DApp: ") << boost::current_exception_diagnostic_information().c_str(); + emit dappError(); + } + +} + +void DappLoader::loadDapp(RLP const& _rlp) +{ + Dapp dapp; + unsigned len = _rlp.itemCountStrict(); + dapp.manifest = loadManifest(_rlp[0].toString()); + for (unsigned c = 1; c < len; ++c) + { + bytesConstRef content = _rlp[c].toBytesConstRef(); + h256 hash = sha3(content); + auto entry = std::find_if(dapp.manifest.entries.cbegin(), dapp.manifest.entries.cend(), [=](ManifestEntry const& _e) { return _e.hash == hash; }); + if (entry != dapp.manifest.entries.cend()) + { + if (entry->path == "/deployment.js") + { + //inject web3 code + QString code; + code += contentsOfQResource(":/js/bignumber.min.js"); + code += "\n"; + code += contentsOfQResource(":/js/webthree.js"); + code += "\n"; + code += contentsOfQResource(":/js/setup.js"); + code += "\n"; + QByteArray res = code.toLatin1(); + bytes b(res.data(), res.data() + res.size()); + b.insert(b.end(), content.begin(), content.end()); + dapp.content[hash] = b; + } + else + dapp.content[hash] = content.toBytes(); + } + else + throw dev::Exception() << errinfo_comment("Dapp content hash does not match"); + } + emit dappReady(dapp); +} + +Manifest DappLoader::loadManifest(std::string const& _manifest) +{ + /// https://github.com/ethereum/go-ethereum/wiki/URL-Scheme + Manifest manifest; + Json::Reader jsonReader; + Json::Value root; + jsonReader.parse(_manifest, root, false); + + Json::Value entries = root["entries"]; + for (Json::ValueIterator it = entries.begin(); it != entries.end(); ++it) + { + Json::Value const& entryValue = *it; + std::string path = entryValue["path"].asString(); + if (path.size() == 0 || path[0] != '/') + path = "/" + path; + std::string contentType = entryValue["contentType"].asString(); + std::string strHash = entryValue["hash"].asString(); + if (strHash.length() == 64) + strHash = "0x" + strHash; + h256 hash = jsToFixed<32>(strHash); + unsigned httpStatus = entryValue["status"].asInt(); + manifest.entries.push_back(ManifestEntry{ path, hash, contentType, httpStatus }); + } + return manifest; +} + +void DappLoader::loadDapp(QString const& _uri) +{ + DappLocation location = resolveAppUri(_uri); + QUrl uri(location.contentUri); + QNetworkRequest request(uri); + m_uriHashes[uri] = location.contentHash; + m_net.get(request); +} + diff --git a/alethzero/DappLoader.h b/alethzero/DappLoader.h new file mode 100644 index 000000000..463b65d0a --- /dev/null +++ b/alethzero/DappLoader.h @@ -0,0 +1,94 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** @file DappLoader.h + * @author Arkadiy Paronyan + * @date 2015 + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace dev +{ + class WebThreeDirect; + class RLP; +} + +struct ManifestEntry +{ + std::string path; + dev::h256 hash; + std::string contentType; + unsigned httpStatus; +}; + +struct Manifest +{ + std::vector entries; +}; + +struct Dapp +{ + Manifest manifest; + std::map content; +}; + + +struct DappLocation +{ + QString canonDomain; + QString path; + QString contentUri; + dev::h256 contentHash; +}; + +///Downloads, unpacks and prepares DApps for hosting +class DappLoader: public QObject +{ + Q_OBJECT +public: + DappLoader(QObject* _parent, dev::WebThreeDirect* _web3); + ///Load a new DApp. Resolves a name with a name reg contract. Asynchronous. dappReady is emitted once everything is read, dappError othervise + ///@param _uri Eth name path + void loadDapp(QString const& _uri); + +signals: + void dappReady(Dapp& _dapp); + void dappError(); + +private slots: + void downloadComplete(QNetworkReply* _reply); + +private: + dev::WebThreeDirect* web3() const { return m_web3; } + DappLocation resolveAppUri(QString const& _uri); + void loadDapp(dev::RLP const& _rlp); + Manifest loadManifest(std::string const& _manifest); + + dev::WebThreeDirect* m_web3; + QNetworkAccessManager m_net; + std::map m_uriHashes; +}; + diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index 88b9103cb..ab8e07e7c 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -19,7 +19,6 @@ * @date 2014 */ -#define QWEBENGINEINSPECTOR 1 #include // Make sure boost/asio.hpp is included before windows.h. @@ -68,6 +67,9 @@ #include "OurWebThreeStubServer.h" #include "Transact.h" #include "Debugger.h" +#include "DappLoader.h" +#include "DappHost.h" +#include "WebPage.h" #include "ui_Main.h" using namespace std; using namespace dev; @@ -116,7 +118,9 @@ Address c_newConfig = Address("c6d9d2cd449a754c494264e1809c50e34d64562b"); Main::Main(QWidget *parent) : QMainWindow(parent), ui(new Ui::Main), - m_transact(this, this) + m_transact(this, this), + m_dappLoader(nullptr), + m_webPage(nullptr) { QtWebEngine::initialize(); setWindowFlags(Qt::Window); @@ -166,10 +170,12 @@ Main::Main(QWidget *parent) : m_server->setIdentities(keysAsVector(owned())); m_server->StartListening(); + WebPage* webPage= new WebPage(this); + m_webPage = webPage; + connect(webPage, &WebPage::consoleMessage, [this](QString const& _msg) { Main::addConsoleMessage(_msg, QString()); }); + ui->webView->setPage(m_webPage); connect(ui->webView, &QWebEngineView::loadFinished, [this]() { -// f->disconnect(); -// f->addToJavaScriptWindowObject("env", this, QWebFrame::QtOwnership); auto f = ui->webView->page(); f->runJavaScript(contentsOfQResource(":/js/bignumber.min.js")); f->runJavaScript(contentsOfQResource(":/js/webthree.js")); @@ -181,6 +187,9 @@ Main::Main(QWidget *parent) : ui->tabWidget->setTabText(0, ui->webView->title()); }); + m_dappHost.reset(new DappHost(8081)); + m_dappLoader = new DappLoader(this, web3()); + connect(m_dappLoader, &DappLoader::dappReady, this, &Main::dappLoaded); // ui->webView->page()->settings()->setAttribute(QWebEngineSettings::DeveloperExtrasEnabled, true); // QWebEngineInspector* inspector = new QWebEngineInspector(); // inspector->setPage(page); @@ -425,13 +434,7 @@ void Main::eval(QString const& _js) s = "" + jsonEv.toString().toHtmlEscaped() + ""; else s = "unknown type"; - m_consoleHistory.push_back(qMakePair(_js, s)); - s = "" Div(Mono "position: absolute; bottom: 0; border: 0px; margin: 0px; width: 100%"); - for (auto const& i: m_consoleHistory) - s += "
>" + i.first.toHtmlEscaped() + "
" - "
 " + i.second + "
"; - s += ""; - ui->jsConsole->setHtml(s); + addConsoleMessage(_js, s); }; ui->webView->page()->runJavaScript("JSON.stringify(___RET)", f2); }; @@ -439,6 +442,17 @@ void Main::eval(QString const& _js) ui->webView->page()->runJavaScript(c, f); } +void Main::addConsoleMessage(QString const& _js, QString const& _s) +{ + m_consoleHistory.push_back(qMakePair(_js, _s)); + QString r = "" Div(Mono "position: absolute; bottom: 0; border: 0px; margin: 0px; width: 100%"); + for (auto const& i: m_consoleHistory) + r += "
>" + i.first.toHtmlEscaped() + "
" + "
 " + i.second + "
"; + r += ""; + ui->jsConsole->setHtml(r); +} + static Public stringToPublic(QString const& _a) { string sn = _a.toStdString(); @@ -780,15 +794,28 @@ void Main::on_jitvm_triggered() void Main::on_urlEdit_returnPressed() { QString s = ui->urlEdit->text(); - QRegExp r("([a-z]+://)?([^/]*)(.*)"); - if (r.exactMatch(s)) - if (r.cap(2).isEmpty()) - s = (r.cap(1).isEmpty() ? "file://" : r.cap(1)) + r.cap(3); + QUrl url(s); + if (url.scheme().isEmpty() || url.scheme() == "eth") + { + try + { + //try do resolve dapp url + m_dappLoader->loadDapp(s); + } + catch (...) + { + qWarning() << boost::current_exception_diagnostic_information().c_str(); + } + } + + if (url.scheme().isEmpty()) + if (url.path().indexOf('/') < url.path().indexOf('.')) + url.setScheme("file"); else - s = (r.cap(1).isEmpty() ? "http://" : r.cap(1)) + lookup(r.cap(2)) + r.cap(3); - else{} - qDebug() << s; - ui->webView->setUrl(s); + url.setScheme("http"); + else {} + qDebug() << url.toString(); + ui->webView->page()->setUrl(url); } void Main::on_nameReg_textChanged() @@ -1835,3 +1862,9 @@ void Main::refreshWhispers() ui->whispers->addItem(item); } } + +void Main::dappLoaded(Dapp& _dapp) +{ + QUrl url = m_dappHost->hostDapp(std::move(_dapp)); + ui->webView->page()->setUrl(url); +} diff --git a/alethzero/MainWin.h b/alethzero/MainWin.h index 378877468..ba89b455a 100644 --- a/alethzero/MainWin.h +++ b/alethzero/MainWin.h @@ -54,8 +54,11 @@ namespace jsonrpc { class HttpServer; } -class QQuickView; +class QWebEnginePage; class OurWebThreeStubServer; +class DappLoader; +class DappHost; +struct Dapp; using WatchHandler = std::function; @@ -99,6 +102,7 @@ public slots: private slots: void eval(QString const& _js); + void addConsoleMessage(QString const& _js, QString const& _s); // Application void on_about_triggered(); @@ -172,6 +176,9 @@ private slots: void refreshBlockChain(); void addNewId(QString _ids); + // Dapps + void dappLoaded(Dapp& _dapp); //qt does not support rvalue refs for signals + signals: void poll(); @@ -234,8 +241,6 @@ private: QString m_privateChain; dev::Address m_nameReg; - QNetworkAccessManager m_webCtrl; - QList> m_consoleHistory; QMutex m_logLock; QString m_logHistory; @@ -248,4 +253,7 @@ private: NatspecHandler m_natSpecDB; Transact m_transact; + std::unique_ptr m_dappHost; + DappLoader* m_dappLoader; + QWebEnginePage* m_webPage; }; diff --git a/alethzero/Transact.cpp b/alethzero/Transact.cpp index df6c5258d..640ffe3d8 100644 --- a/alethzero/Transact.cpp +++ b/alethzero/Transact.cpp @@ -175,7 +175,7 @@ void Transact::rejigData() m_data = fromHex(src); else if (sourceIsSolidity(src)) { - dev::solidity::CompilerStack compiler; + dev::solidity::CompilerStack compiler(true); try { // compiler.addSources(dev::solidity::StandardSources); @@ -287,7 +287,7 @@ void Transact::on_send_clicked() if (sourceIsSolidity(src)) try { - dev::solidity::CompilerStack compiler; + dev::solidity::CompilerStack compiler(true); m_data = compiler.compile(src, ui->optimize->isChecked()); for (string const& s: compiler.getContractNames()) { diff --git a/alethzero/WebPage.cpp b/alethzero/WebPage.cpp new file mode 100644 index 000000000..f9a307a53 --- /dev/null +++ b/alethzero/WebPage.cpp @@ -0,0 +1,29 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** @file WebPage.cpp + * @author Arkadiy Paronyan arkadiy@ethdev.com> + * @date 2015 + */ + +#pragma GCC diagnostic ignored "-Wpedantic" //QtWebEngineWidgets/qwebenginecertificateerror.h:78:348: error: extra ';' +#include "WebPage.h" + +void WebPage::javaScriptConsoleMessage(QWebEnginePage::JavaScriptConsoleMessageLevel _level, const QString& _message, int _lineNumber, const QString& _sourceID) +{ + QString prefix = _level == QWebEnginePage::ErrorMessageLevel ? "error" : _level == QWebEnginePage::WarningMessageLevel ? "warning" : ""; + emit consoleMessage(QString("%1(%2:%3):%4").arg(prefix).arg(_sourceID).arg(_lineNumber).arg(_message)); +} diff --git a/alethzero/WebPage.h b/alethzero/WebPage.h new file mode 100644 index 000000000..bfb67e749 --- /dev/null +++ b/alethzero/WebPage.h @@ -0,0 +1,37 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** @file WebPage.h + * @author Arkadiy Paronyan arkadiy@ethdev.com> + * @date 2015 + */ + +#pragma once + +#include +#include + +class WebPage: public QWebEnginePage +{ + Q_OBJECT +public: + WebPage(QObject* _parent): QWebEnginePage(_parent) { } +signals: + void consoleMessage(QString const& _msg); + +protected: + void javaScriptConsoleMessage(QWebEnginePage::JavaScriptConsoleMessageLevel _level, const QString& _message, int _lineNumber, const QString& _sourceID) override; +}; diff --git a/mix/FileIo.cpp b/mix/FileIo.cpp index 449512975..bf0351ee3 100644 --- a/mix/FileIo.cpp +++ b/mix/FileIo.cpp @@ -158,6 +158,7 @@ QStringList FileIo::makePackage(QString const& _deploymentFolder) } rlpStr.appendList(k); + manifest["entries"] = entries; std::stringstream jsonStr; jsonStr << manifest; QByteArray b = QString::fromStdString(jsonStr.str()).toUtf8(); @@ -166,7 +167,6 @@ QStringList FileIo::makePackage(QString const& _deploymentFolder) for (unsigned int k = 0; k < files.size(); k++) rlpStr.append(files.at(k)); - manifest["entries"] = entries; bytes dapp = rlpStr.out(); dev::h256 dappHash = dev::sha3(dapp); //encrypt diff --git a/mix/qml/js/ProjectModel.js b/mix/qml/js/ProjectModel.js index d308119ae..35c793dd3 100644 --- a/mix/qml/js/ProjectModel.js +++ b/mix/qml/js/ProjectModel.js @@ -377,8 +377,6 @@ function finalizeDeployment(deploymentId, addresses) { else insertAt += 6; html = html.substr(0, insertAt) + - "" + - "" + "" + html.substr(insertAt); fileIo.writeFile(deploymentDir + doc.fileName, html); @@ -388,9 +386,8 @@ function finalizeDeployment(deploymentId, addresses) { } //write deployment js var deploymentJs = - "// Autogenerated by Mix\n" + - "web3 = require(\"web3\");\n" + - "contracts = {};\n"; + "// Autogenerated by Mix\n" + + "contracts = {};\n"; for (var c in codeModel.contracts) { var contractAccessor = "contracts[\"" + codeModel.contracts[c].contract.name + "\"]"; deploymentJs += contractAccessor + " = {\n" + @@ -400,9 +397,6 @@ function finalizeDeployment(deploymentId, addresses) { contractAccessor + ".contract = web3.eth.contract(" + contractAccessor + ".address, " + contractAccessor + ".interface);\n"; } fileIo.writeFile(deploymentDir + "deployment.js", deploymentJs); - //copy scripts - fileIo.copyFile("qrc:///js/bignumber.min.js", deploymentDir + "bignumber.min.js"); - fileIo.copyFile("qrc:///js/webthree.js", deploymentDir + "ethereum.js"); deploymentAddresses = addresses; saveProject(); @@ -435,7 +429,7 @@ function checkEthPath(dappUrl, callBack) //register() jsonrpc: "2.0", method: "eth_call", - params: [ { "from": deploymentDialog.currentAccount, "to": '0x' + deploymentDialog.eth, "data": "0x6be16bed" + str.encodeValueAsString() } ], + params: [ { "gas": 150000, "from": deploymentDialog.currentAccount, "to": '0x' + deploymentDialog.eth, "data": "0x6be16bed" + str.encodeValueAsString() } ], id: jsonRpcRequestId++ }); rpcCall(requests, function (httpRequest, response) { @@ -472,7 +466,7 @@ function checkRegistration(dappUrl, addr, callBack) //getOwner() jsonrpc: "2.0", method: "eth_call", - params: [ { "from": deploymentDialog.currentAccount, "to": '0x' + addr, "data": "0x893d20e8" } ], + params: [ { "gas" : 2000, "from": deploymentDialog.currentAccount, "to": '0x' + addr, "data": "0x893d20e8" } ], id: jsonRpcRequestId++ }); @@ -537,7 +531,7 @@ function checkRegistration(dappUrl, addr, callBack) //setRegister() jsonrpc: "2.0", method: "eth_transact", - params: [ { "from": deploymentDialog.currentAccount, "gas": 2000, "to": '0x' + addr, "data": "0x96077307" + crLevel + deploymentDialog.pad(newCtrAddress) } ], + params: [ { "from": deploymentDialog.currentAccount, "gas": 30000, "to": '0x' + addr, "data": "0x96077307" + crLevel + deploymentDialog.pad(newCtrAddress) } ], id: jsonRpcRequestId++ }); @@ -570,7 +564,7 @@ function registerContentHash(registrar, callBack) //setContent() jsonrpc: "2.0", method: "eth_transact", - params: [ { "from": deploymentDialog.currentAccount, "gas": 2000, "gasPrice": "10", "to": '0x' + registrar, "data": "0x5d574e32" + paramTitle.encodeValueAsString() + deploymentDialog.packageHash } ], + params: [ { "from": deploymentDialog.currentAccount, "gas": 30000, "gasPrice": "10", "to": '0x' + registrar, "data": "0x5d574e32" + paramTitle.encodeValueAsString() + deploymentDialog.packageHash } ], id: jsonRpcRequestId++ }); rpcCall(requests, function (httpRequest, response) { @@ -587,7 +581,7 @@ function registerToUrlHint() //urlHint => suggestUrl jsonrpc: "2.0", method: "eth_transact", - params: [ { "to": '0x' + deploymentDialog.urlHintContract, "gas": 2000, "data": "0x4983e19c" + deploymentDialog.packageHash + paramUrlHttp.encodeValueAsString() } ], + params: [ { "to": '0x' + deploymentDialog.urlHintContract, "gas": 30000, "data": "0x4983e19c" + deploymentDialog.packageHash + paramUrlHttp.encodeValueAsString() } ], id: jsonRpcRequestId++ }); From 33d4f6049957a5bb4f89b26cb4c0c0e9c9466ab1 Mon Sep 17 00:00:00 2001 From: arkpar Date: Tue, 10 Mar 2015 18:57:07 +0100 Subject: [PATCH 079/100] gcc build fixed --- alethzero/WebPage.cpp | 1 - alethzero/WebPage.h | 3 +++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/alethzero/WebPage.cpp b/alethzero/WebPage.cpp index f9a307a53..08c5c3cc6 100644 --- a/alethzero/WebPage.cpp +++ b/alethzero/WebPage.cpp @@ -19,7 +19,6 @@ * @date 2015 */ -#pragma GCC diagnostic ignored "-Wpedantic" //QtWebEngineWidgets/qwebenginecertificateerror.h:78:348: error: extra ';' #include "WebPage.h" void WebPage::javaScriptConsoleMessage(QWebEnginePage::JavaScriptConsoleMessageLevel _level, const QString& _message, int _lineNumber, const QString& _sourceID) diff --git a/alethzero/WebPage.h b/alethzero/WebPage.h index bfb67e749..5f1bb1dc8 100644 --- a/alethzero/WebPage.h +++ b/alethzero/WebPage.h @@ -22,7 +22,10 @@ #pragma once #include +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpedantic" //QtWebEngineWidgets/qwebenginecertificateerror.h:78:348: error: extra ';' #include +#pragma GCC diagnostic pop class WebPage: public QWebEnginePage { From bbb72e7e8470a379abaa1ac763be1a7733c5a789 Mon Sep 17 00:00:00 2001 From: arkpar Date: Tue, 10 Mar 2015 18:11:40 +0200 Subject: [PATCH 080/100] fixed msvc build --- alethzero/DappHost.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/alethzero/DappHost.cpp b/alethzero/DappHost.cpp index 22ba41c6c..fd73c1b17 100644 --- a/alethzero/DappHost.cpp +++ b/alethzero/DappHost.cpp @@ -19,11 +19,11 @@ * @date 2015 */ +#include "DappHost.h" +#include #include #include #include -#include -#include "DappHost.h" using namespace dev; @@ -61,7 +61,6 @@ void DappHost::sendOptionsResponse(MHD_Connection* _connection) { MHD_Response *result = MHD_create_response_from_data(0, NULL, 0, 1); MHD_add_response_header(result, "Allow", "GET, OPTIONS"); - //MHD_add_response_header(result, "Access-Control-Allow-Origin", "*"); MHD_add_response_header(result, "Access-Control-Allow-Headers", "origin, content-type, accept"); MHD_add_response_header(result, "DAV", "1"); MHD_queue_response(_connection, MHD_HTTP_OK, result); @@ -109,7 +108,6 @@ void DappHost::sendResponse(std::string const& _url, MHD_Connection* _connection MHD_Response *result = MHD_create_response_from_data(response.size(), const_cast(response.data()), 0, 1); if (!contentType.empty()) MHD_add_response_header(result, "Content-Type", contentType.c_str()); - //MHD_add_response_header(result, "Access-Control-Allow-Origin", "*"); MHD_queue_response(_connection, code, result); MHD_destroy_response(result); } From d9be32d6311635462dc799047537d7dc4885ae82 Mon Sep 17 00:00:00 2001 From: yann300 Date: Tue, 10 Mar 2015 17:36:14 +0100 Subject: [PATCH 081/100] small changes --- mix/AppContext.cpp | 3 +-- ...roxymodel.cpp => SortFilterProxyModel.cpp} | 25 ++++++++++--------- ...terproxymodel.h => SortFilterProxyModel.h} | 0 3 files changed, 14 insertions(+), 14 deletions(-) rename mix/{Sortfilterproxymodel.cpp => SortFilterProxyModel.cpp} (89%) rename mix/{Sortfilterproxymodel.h => SortFilterProxyModel.h} (100%) diff --git a/mix/AppContext.cpp b/mix/AppContext.cpp index b649ee4c2..991fd334a 100644 --- a/mix/AppContext.cpp +++ b/mix/AppContext.cpp @@ -22,7 +22,6 @@ * - KeyEventManager */ -#include #include #include #include @@ -38,7 +37,7 @@ #include "QVariableDefinition.h" #include "HttpServer.h" #include "AppContext.h" -#include "Sortfilterproxymodel.h" +#include "SortFilterProxyModel.h" using namespace dev; using namespace dev::eth; diff --git a/mix/Sortfilterproxymodel.cpp b/mix/SortFilterProxyModel.cpp similarity index 89% rename from mix/Sortfilterproxymodel.cpp rename to mix/SortFilterProxyModel.cpp index 64689842b..6fb2cca0c 100644 --- a/mix/Sortfilterproxymodel.cpp +++ b/mix/SortFilterProxyModel.cpp @@ -21,7 +21,7 @@ */ -#include "Sortfilterproxymodel.h" +#include "SortFilterProxyModel.h" #include #include @@ -29,8 +29,8 @@ using namespace dev::mix; SortFilterProxyModel::SortFilterProxyModel(QObject* _parent) : QSortFilterProxyModel(_parent) { - connect(this, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SIGNAL(countChanged())); - connect(this, SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SIGNAL(countChanged())); + connect(this, &SortFilterProxyModel::rowsInserted, this, &SortFilterProxyModel::countChanged); + connect(this, &SortFilterProxyModel::rowsRemoved, this, &SortFilterProxyModel::countChanged); } int SortFilterProxyModel::count() const @@ -45,7 +45,7 @@ QObject* SortFilterProxyModel::source() const void SortFilterProxyModel::setSource(QObject* _source) { - setSourceModel(qobject_cast(_source)); + setSourceModel(qobject_cast(_source)); } QByteArray SortFilterProxyModel::sortRole() const @@ -87,10 +87,12 @@ QJSValue SortFilterProxyModel::get(int _idx) const { QJSEngine *engine = qmlEngine(this); QJSValue value = engine->newObject(); - if (_idx >= 0 && _idx < count()) { + if (_idx >= 0 && _idx < count()) + { QHash roles = roleNames(); QHashIterator it(roles); - while (it.hasNext()) { + while (it.hasNext()) + { it.next(); value.setProperty(QString::fromUtf8(it.value()), data(index(_idx, 0), it.key()).toString()); } @@ -102,7 +104,8 @@ int SortFilterProxyModel::roleKey(QByteArray const& _role) const { QHash roles = roleNames(); QHashIterator it(roles); - while (it.hasNext()) { + while (it.hasNext()) + { it.next(); if (it.value() == _role) return it.key(); @@ -119,12 +122,10 @@ QHash SortFilterProxyModel::roleNames() const bool SortFilterProxyModel::filterAcceptsRow(int _sourceRow, QModelIndex const& _sourceParent) const { - - QRegExp rx = filterRegExp(); - QAbstractItemModel *model = sourceModel(); + QAbstractItemModel* model = sourceModel(); QModelIndex sourceIndex = model->index(_sourceRow, 0, _sourceParent); - if (!sourceIndex.isValid()) - return true; + if (!sourceIndex.isValid()) + return true; QString keyType = model->data(sourceIndex, roleKey(type.toUtf8())).toString(); QString keyContent = model->data(sourceIndex, roleKey(content.toUtf8())).toString(); diff --git a/mix/Sortfilterproxymodel.h b/mix/SortFilterProxyModel.h similarity index 100% rename from mix/Sortfilterproxymodel.h rename to mix/SortFilterProxyModel.h From 8a6025ed8a3cfa3b85f4b6d95ad710cedabda553 Mon Sep 17 00:00:00 2001 From: CJentzsch Date: Mon, 9 Mar 2015 10:28:48 +0100 Subject: [PATCH 082/100] blockchain tests fixes --- test/TestHelper.cpp | 41 ++++++++++++++----------------------- test/TestHelper.h | 8 ++++++++ test/bcUncleTestFiller.json | 18 +--------------- test/blockchain.cpp | 14 ++++++++++++- 4 files changed, 37 insertions(+), 44 deletions(-) diff --git a/test/TestHelper.cpp b/test/TestHelper.cpp index befd571ea..9ac64107d 100644 --- a/test/TestHelper.cpp +++ b/test/TestHelper.cpp @@ -181,46 +181,35 @@ void ImportTest::exportTest(bytes const& _output, State const& _statePost) m_TestObject["logs"] = exportLog(_statePost.pending().size() ? _statePost.log(0) : LogEntries()); // export post state - json_spirit::mObject postState; - - for (auto const& a: _statePost.addresses()) - { - json_spirit::mObject o; - o["balance"] = toString(_statePost.balance(a.first)); - o["nonce"] = toString(_statePost.transactionsFrom(a.first)); - { - json_spirit::mObject store; - for (auto const& s: _statePost.storage(a.first)) - store["0x"+toHex(toCompactBigEndian(s.first))] = "0x"+toHex(toCompactBigEndian(s.second)); - o["storage"] = store; - } - o["code"] = "0x" + toHex(_statePost.code(a.first)); - - postState[toString(a.first)] = o; - } - m_TestObject["post"] = json_spirit::mValue(postState); + m_TestObject["post"] = fillJsonWithState(_statePost); m_TestObject["postStateRoot"] = toHex(_statePost.rootHash().asBytes()); // export pre state - json_spirit::mObject preState; + m_TestObject["pre"] = fillJsonWithState(m_statePre); +} + +json_spirit::mObject fillJsonWithState(State _state) +{ + // export pre state + json_spirit::mObject oState; - for (auto const& a: m_statePre.addresses()) + for (auto const& a: _state.addresses()) { json_spirit::mObject o; - o["balance"] = toString(m_statePre.balance(a.first)); - o["nonce"] = toString(m_statePre.transactionsFrom(a.first)); + o["balance"] = toString(_state.balance(a.first)); + o["nonce"] = toString(_state.transactionsFrom(a.first)); { json_spirit::mObject store; - for (auto const& s: m_statePre.storage(a.first)) + for (auto const& s: _state.storage(a.first)) store["0x"+toHex(toCompactBigEndian(s.first))] = "0x"+toHex(toCompactBigEndian(s.second)); o["storage"] = store; } - o["code"] = "0x" + toHex(m_statePre.code(a.first)); + o["code"] = "0x" + toHex(_state.code(a.first)); - preState[toString(a.first)] = o; + oState[toString(a.first)] = o; } - m_TestObject["pre"] = json_spirit::mValue(preState); + return oState; } u256 toInt(json_spirit::mValue const& _v) diff --git a/test/TestHelper.h b/test/TestHelper.h index 0f23f945c..d378848a2 100644 --- a/test/TestHelper.h +++ b/test/TestHelper.h @@ -117,6 +117,13 @@ private: json_spirit::mObject& m_TestObject; }; +class ZeroGasPricer: public eth::GasPricer +{ +protected: + u256 ask(eth::State const&) const override { return 0; } + u256 bid(eth::TransactionPriority = eth::TransactionPriority::Medium) const override { return 0; } +}; + // helping functions u256 toInt(json_spirit::mValue const& _v); byte toByte(json_spirit::mValue const& _v); @@ -136,6 +143,7 @@ void userDefinedTest(std::string testTypeFlag, std::function void checkAddresses(mapType& _expectedAddrs, mapType& _resultAddrs) diff --git a/test/bcUncleTestFiller.json b/test/bcUncleTestFiller.json index 639051f2b..21a987e16 100644 --- a/test/bcUncleTestFiller.json +++ b/test/bcUncleTestFiller.json @@ -265,23 +265,7 @@ "uncleHash" : "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347" }, { - "bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "coinbase" : "0000000000000000000000000000000000000000", - "difficulty" : "131072", - "extraData" : "0x", - "gasLimit" : "99902343", - "gasUsed" : "0", - "hash" : "9de9879b6a81d1b6c4993c63c90a3c9d1e775f14572694778e828bc64972ae04", - "mixHash" : "b557f905d29ed0fca99d65d0adcce698dee97cf72a13c7cd8d7a7826b8eee770", - "nonce" : "18a524c1790fa83b", - "number" : "2", - "parentHash" : "6134fc6b5d99ee03c4aab1592640f6f9dcbc850668d75d631aee34989b938fae", - "receiptTrie" : "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", - "seedHash" : "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", - "stateRoot" : "ff640b30d613c35dad43e3693329e1b1ee6350f989cf46a288025a1cbfdab9cd", - "timestamp" : "0x54c98c82", - "transactionsTrie" : "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", - "uncleHash" : "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347" + "theSameAsBefore" : "1" } ] } diff --git a/test/blockchain.cpp b/test/blockchain.cpp index 0e1128ef9..27d15c60c 100644 --- a/test/blockchain.cpp +++ b/test/blockchain.cpp @@ -52,6 +52,7 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin) ImportTest importer(o["pre"].get_obj()); State state(Address(), OverlayDB(), BaseState::Empty); importer.importState(o["pre"].get_obj(), state); + o["pre"] = fillJsonWithState(state); state.commit(); if (_fillin) @@ -87,7 +88,7 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin) // get txs TransactionQueue txs; - TrivialGasPricer gp; + ZeroGasPricer gp; BOOST_REQUIRE(blObj.count("transactions")); for (auto const& txObj: blObj["transactions"].get_array()) { @@ -101,10 +102,18 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin) BlockQueue uncleBlockQueue; mArray aUncleList; vector vBiUncles; + mObject uncleHeaderObj_pre; for (auto const& uHObj: blObj["uncleHeaders"].get_array()) { mObject uncleHeaderObj = uHObj.get_obj(); + if ( uncleHeaderObj.count("theSameAsBefore") ) + { + writeBlockHeaderToJson(uncleHeaderObj_pre, vBiUncles[vBiUncles.size()-1]); + aUncleList.push_back(uncleHeaderObj_pre); + vBiUncles.push_back(vBiUncles[vBiUncles.size()-1]); + continue; + } BlockInfo uncleBlockFromFields = constructBlock(uncleHeaderObj); // make uncle header valid @@ -123,6 +132,8 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin) cnote << "import uncle in blockQueue"; RLPStream uncle = createFullBlockFromHeader(uncleBlockFromFields); uncleBlockQueue.import(&uncle.out(), bc); + + uncleHeaderObj_pre = uncleHeaderObj; } blObj["uncleHeaders"] = aUncleList; @@ -579,6 +590,7 @@ void updatePoW(BlockInfo& _bi) ret = pow.mine(_bi, 10000, true, true); // tie(ret, blockFromFields.nonce) Ethash::assignResult(ret.second, _bi); } + _bi.hash = _bi.headerHash(WithNonce); } void writeBlockHeaderToJson(mObject& _o, const BlockInfo& _bi) From 64e956f6eb9ececc029e5a5aa8b6b180568d8e42 Mon Sep 17 00:00:00 2001 From: CJentzsch Date: Mon, 9 Mar 2015 14:50:09 +0100 Subject: [PATCH 083/100] style --- test/bcUncleTestFiller.json | 2 +- test/blockchain.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/bcUncleTestFiller.json b/test/bcUncleTestFiller.json index 21a987e16..73d1e5487 100644 --- a/test/bcUncleTestFiller.json +++ b/test/bcUncleTestFiller.json @@ -265,7 +265,7 @@ "uncleHash" : "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347" }, { - "theSameAsBefore" : "1" + "sameAsPreviousSibling" : "1" } ] } diff --git a/test/blockchain.cpp b/test/blockchain.cpp index 27d15c60c..eca08d09e 100644 --- a/test/blockchain.cpp +++ b/test/blockchain.cpp @@ -107,7 +107,7 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin) for (auto const& uHObj: blObj["uncleHeaders"].get_array()) { mObject uncleHeaderObj = uHObj.get_obj(); - if ( uncleHeaderObj.count("theSameAsBefore") ) + if (uncleHeaderObj.count("sameAsPreviousSibling")) { writeBlockHeaderToJson(uncleHeaderObj_pre, vBiUncles[vBiUncles.size()-1]); aUncleList.push_back(uncleHeaderObj_pre); From d1f3f1bf9e91bc21f5a4588b833df7cf10daa65f Mon Sep 17 00:00:00 2001 From: The Gitter Badger Date: Tue, 10 Mar 2015 18:07:09 +0000 Subject: [PATCH 084/100] Added Gitter badge --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 6cd9c7adf..2bb57566c 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ ## Ethereum C++ Client. +[![Join the chat at https://gitter.im/ethereum/cpp-ethereum](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/ethereum/cpp-ethereum?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) + By Gav Wood et al, 2013, 2014, 2015. | Linux | OSX | Windows From cdb4e84bfbf182250a50b2860efd889df7949068 Mon Sep 17 00:00:00 2001 From: yann300 Date: Tue, 10 Mar 2015 19:24:20 +0100 Subject: [PATCH 085/100] - Clear code model when project changes (#1262). - Small changes. --- mix/CodeModel.h | 2 ++ mix/qml/ProjectModel.qml | 10 +++++++++- mix/qml/StateListModel.qml | 5 +++++ mix/qml/js/ProjectModel.js | 1 - 4 files changed, 16 insertions(+), 2 deletions(-) diff --git a/mix/CodeModel.h b/mix/CodeModel.h index de9a07bab..92111996c 100644 --- a/mix/CodeModel.h +++ b/mix/CodeModel.h @@ -164,6 +164,8 @@ public: /// Find a contract by document id /// @returns CompiledContract object or null if not found Q_INVOKABLE CompiledContract* contractByDocumentId(QString _documentId) const; + /// Reset code model + Q_INVOKABLE void reset() { reset(QVariantMap()); } signals: /// Emited on compilation state change diff --git a/mix/qml/ProjectModel.qml b/mix/qml/ProjectModel.qml index d3b4070aa..cca2c834b 100644 --- a/mix/qml/ProjectModel.qml +++ b/mix/qml/ProjectModel.qml @@ -64,7 +64,7 @@ Item { Connections { target: appContext onAppLoaded: { - if (projectSettings.lastProjectPath) + if (projectSettings.lastProjectPath && projectSettings.lastProjectPath !== "") projectModel.loadProject(projectSettings.lastProjectPath) } } @@ -130,6 +130,14 @@ Item { id: projectStateListModel } + Connections + { + target: projectModel + onProjectClosed: { + projectSettings.lastProjectPath = ""; + } + } + Settings { id: projectSettings property string lastProjectPath; diff --git a/mix/qml/StateListModel.qml b/mix/qml/StateListModel.qml index 61307826c..6deeb9790 100644 --- a/mix/qml/StateListModel.qml +++ b/mix/qml/StateListModel.qml @@ -134,6 +134,7 @@ Item { onProjectClosed: { stateListModel.clear(); stateList = []; + codeModel.reset(); } onProjectLoading: stateListModel.loadStatesFromProject(projectData); onProjectSaving: { @@ -148,6 +149,7 @@ Item { state.title = qsTr("Default"); projectData.states = [ state ]; projectData.defaultStateIndex = 0; + stateListModel.loadStatesFromProject(projectData); } } @@ -264,6 +266,7 @@ Item { defaultStateIndex--; save(); + } function save() { @@ -284,6 +287,8 @@ Item { else defaultStateIndex = 0; var items = projectData.states; + stateListModel.clear(); + stateList = []; for(var i = 0; i < items.length; i++) { var item = fromPlainStateItem(items[i]); stateListModel.append(item); diff --git a/mix/qml/js/ProjectModel.js b/mix/qml/js/ProjectModel.js index d308119ae..d4e3a8417 100644 --- a/mix/qml/js/ProjectModel.js +++ b/mix/qml/js/ProjectModel.js @@ -105,7 +105,6 @@ function loadProject(path) { contractSources[doc.documentId] = fileIo.readFile(doc.path); } codeModel.reset(contractSources); - } function addFile(fileName) { From 667af3a13648859a05c2bdc6783a54dd5e68082e Mon Sep 17 00:00:00 2001 From: winsvega Date: Tue, 10 Mar 2015 14:38:17 +0300 Subject: [PATCH 086/100] State Init And Transaction Tests fixed to poc9 --- test/stInitCodeTestFiller.json | 46 +++++----- test/stTransactionTestFiller.json | 147 +++++++++++++++++++++++++++--- test/ttTransactionTestFiller.json | 6 +- 3 files changed, 162 insertions(+), 37 deletions(-) diff --git a/test/stInitCodeTestFiller.json b/test/stInitCodeTestFiller.json index 902290121..cb1b4d14a 100644 --- a/test/stInitCodeTestFiller.json +++ b/test/stInitCodeTestFiller.json @@ -1,5 +1,5 @@ { - "TransactionContractCreation" : { + "TransactionCreateRandomInitCode" : { "env" : { "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", "currentDifficulty" : "45678256", @@ -58,7 +58,7 @@ "nonce" : "0", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", "to" : "", - "value" : "1" + "value" : "100" } }, @@ -66,7 +66,7 @@ "env" : { "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", "currentDifficulty" : "45678256", - "currentGasLimit" : "1000000", + "currentGasLimit" : "100000000", "currentNumber" : "0", "currentTimestamp" : 1, "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6" @@ -74,7 +74,7 @@ "pre" : { "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { - "balance" : "70000", + "balance" : "1000000", "code" : "", "nonce" : "0", "storage" : { @@ -84,7 +84,7 @@ "transaction" : { "data" : "0x600a80600c6000396000f200600160008035811a8100", - "gasLimit" : "21590", + "gasLimit" : "22000", "gasPrice" : "3", "nonce" : "0", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", @@ -123,7 +123,7 @@ } }, - "TransactionSuicideInitCode" : { + "TransactionCreateAutoSuicideContract" : { "env" : { "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", "currentDifficulty" : "45678256", @@ -150,11 +150,11 @@ "nonce" : "0", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", "to" : "", - "value" : "1" + "value" : "15" } }, - "TransactionStopInitCode" : { + "TransactionCreateStopInInitcode" : { "env" : { "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", "currentDifficulty" : "45678256", @@ -185,7 +185,7 @@ } }, - "TransactionCreateSuicideContract" : { + "TransactionCreateSuicideInInitcode" : { "env" : { "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", "currentDifficulty" : "45678256", @@ -220,7 +220,7 @@ "env" : { "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", "currentDifficulty" : "45678256", - "currentGasLimit" : "1000000", + "currentGasLimit" : "100000000", "currentNumber" : "0", "currentTimestamp" : 1, "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6" @@ -235,7 +235,7 @@ }, "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { - "balance" : "100000", + "balance" : "1000000", "code" : "", "nonce" : "0", "storage" : { @@ -245,7 +245,7 @@ "transaction" : { "data" : "0x00", - "gasLimit" : "40000", + "gasLimit" : "100000", "gasPrice" : "1", "nonce" : "0", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", @@ -258,7 +258,7 @@ "env" : { "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", "currentDifficulty" : "45678256", - "currentGasLimit" : "1000000", + "currentGasLimit" : "100000000", "currentNumber" : "0", "currentTimestamp" : 1, "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6" @@ -273,7 +273,7 @@ }, "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { - "balance" : "100000", + "balance" : "1000000", "code" : "", "nonce" : "0", "storage" : { @@ -283,7 +283,7 @@ "transaction" : { "data" : "0x00", - "gasLimit" : "40000", + "gasLimit" : "400000", "gasPrice" : "1", "nonce" : "0", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", @@ -347,7 +347,7 @@ "nonce": "0", "//": "(CREATE 0 64 32)", "//": "{[[0]] 12 (MSTORE 32 0x602060406000f0)(RETURN 57 7)}", - "code": "{(MSTORE 0 0x600c60005566602060406000f060205260076039f3)[[0]](CREATE 1 11 21)(CALL 500 (SLOAD 0) 1 0 0 0 0)}", + "code": "{(MSTORE 0 0x600c60005566602060406000f060205260076039f3)[[0]](CREATE 1 11 21)(CALL 50000 (SLOAD 0) 1 0 0 0 0)}", "storage": {} }, @@ -427,7 +427,7 @@ "nonce": "0", "//": "(CREATE 0 64 32)", "//": "{[[0]] 12 (MSTORE 32 0x602060406000f0)(RETURN 57 7)}", - "code": "{(MSTORE 0 0x600c60005566602060406000f060205260076039f3)[[0]](CREATE 1 11 21)(CALL 0 (SLOAD 0) 1 0 0 0 0)}", + "code": "{(MSTORE 0 0x600c60005566602060406000f060205260076039f3)[[0]](CREATE 1 11 21)(CALL 100000 (SLOAD 0) 1 0 0 0 0)}", "storage": {} }, @@ -442,7 +442,7 @@ "transaction" : { "data" : "0x00", - "gasLimit" : "20000000", + "gasLimit" : "1000000", "gasPrice" : "1", "nonce" : "0", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", @@ -463,11 +463,11 @@ "pre" : { "095e7baea6a6c7c4c2dfeb977efac326af552d87": { - "balance": "1000", + "balance": "10000", "nonce": "0", "//": "(CREATE 0 64 32)", "//": "{[[0]] 12 (MSTORE 32 0x602060406000f0)(RETURN 57 7)}", - "code": "{(MSTORE 0 0x600c60005566602060406000f060205260076039f3)[[0]](CREATE 1001 11 21)}", + "code": "{(MSTORE 0 0x600c60005566602060406000f060205260076039f3)[[0]](CREATE 100000 11 21)}", "storage": {} }, @@ -534,7 +534,7 @@ "env" : { "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", "currentDifficulty" : "45678256", - "currentGasLimit" : "100000000", + "currentGasLimit" : "1000000000", "currentNumber" : "0", "currentTimestamp" : 1, "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6" @@ -542,7 +542,7 @@ "pre" : { "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { - "balance" : "100000", + "balance" : "1000000", "code" : "{(MSTORE 0 0x15)(CALL 7000 0xb94f5374fce5edbc8e2a8697c15331677e6ebf0b 0 0 32 32 32) (RETURN 0 64)}", "nonce" : "0", "storage" : { @@ -560,7 +560,7 @@ "transaction" : { "data" : "", - "gasLimit" : "25000", + "gasLimit" : "250000", "gasPrice" : "1", "nonce" : "0", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", diff --git a/test/stTransactionTestFiller.json b/test/stTransactionTestFiller.json index 9bd7436bc..a69f0df27 100644 --- a/test/stTransactionTestFiller.json +++ b/test/stTransactionTestFiller.json @@ -514,7 +514,7 @@ "transaction" : { "data" : "", - "gasLimit" : "21100", + "gasLimit" : "23000", "gasPrice" : "1", "nonce" : "", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", @@ -535,7 +535,7 @@ "pre" : { "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { - "balance" : "22000", + "balance" : "1000000", "code" : "", "nonce" : "0", "storage" : { @@ -545,7 +545,7 @@ "c94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { "balance" : "10", "//" : "gas = 19 going OOG, gas = 20 fine", - "code" : "{ (CALL 19 0 1 0 0 0 0) }", + "code" : "{ (CALL 40000 0 1 0 0 0 0) }", "nonce" : "0", "storage" : { } @@ -574,7 +574,67 @@ "transaction" : { "data" : "", - "gasLimit" : "21100", + "gasLimit" : "160000", + "gasPrice" : "1", + "nonce" : "", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "c94f5374fce5edbc8e2a8697c15331677e6ebf0b", + "value" : "10" + } + }, + + "InternlCallStoreClearsSucces" : { + "env" : { + "currentCoinbase" : "b94f5374fce5edbc8e2a8697c15331677e6ebf0b", + "currentDifficulty" : "45678256", + "currentGasLimit" : "1000000", + "currentNumber" : "0", + "currentTimestamp" : 1, + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6" + }, + "pre" : + { + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000", + "code" : "", + "nonce" : "0", + "storage" : { + } + }, + + "c94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "10", + "//" : "gas = 19 going OOG, gas = 20 fine", + "code" : "{ (CALL 100000 0 1 0 0 0 0) }", + "nonce" : "0", + "storage" : { + } + }, + + "0000000000000000000000000000000000000000" : { + "balance" : "0", + "code" : "{(SSTORE 0 0)(SSTORE 1 0)(SSTORE 2 0)(SSTORE 3 0)(SSTORE 4 0)(SSTORE 5 0)(SSTORE 6 0)(SSTORE 7 0)(SSTORE 8 0)(SSTORE 9 0)}", + "nonce" : "0", + "storage" : { + "0x" : "0x0c", + "0x01" : "0x0c", + "0x02" : "0x0c", + "0x03" : "0x0c", + "0x04" : "0x0c", + "0x05" : "0x0c", + "0x06" : "0x0c", + "0x07" : "0x0c", + "0x08" : "0x0c", + "0x09" : "0x0c" + } + } + + }, + + "transaction" : + { + "data" : "", + "gasLimit" : "160000", "gasPrice" : "1", "nonce" : "", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", @@ -605,7 +665,7 @@ "c94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { "balance" : "10", "//" : "gas = 19 going OOG, gas = 20 fine", - "code" : "{(SSTORE 0 0)(SSTORE 1 0)(SSTORE 2 0)(SSTORE 3 0) (CALL 19 0 1 0 0 0 0) }", + "code" : "{(SSTORE 0 0)(SSTORE 1 0)(SSTORE 2 0)(SSTORE 3 0) (CALL 20000 0 1 0 0 0 0) }", "nonce" : "0", "storage" : { "0x" : "0x0c", @@ -639,7 +699,72 @@ "transaction" : { "data" : "", - "gasLimit" : "23000", + "gasLimit" : "200000", + "gasPrice" : "1", + "nonce" : "", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "c94f5374fce5edbc8e2a8697c15331677e6ebf0b", + "value" : "10" + } + }, + + "StoreClearsAndInternlCallStoreClearsSuccess" : { + "env" : { + "currentCoinbase" : "b94f5374fce5edbc8e2a8697c15331677e6ebf0b", + "currentDifficulty" : "45678256", + "currentGasLimit" : "10000000", + "currentNumber" : "0", + "currentTimestamp" : 1, + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6" + }, + "pre" : + { + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "500000", + "code" : "", + "nonce" : "0", + "storage" : { + } + }, + + "c94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "10", + "//" : "gas = 19 going OOG, gas = 20 fine", + "code" : "{(SSTORE 0 0)(SSTORE 1 0)(SSTORE 2 0)(SSTORE 3 0) (CALL 50000 0 1 0 0 0 0) }", + "nonce" : "0", + "storage" : { + "0x" : "0x0c", + "0x01" : "0x0c", + "0x02" : "0x0c", + "0x03" : "0x0c", + "0x04" : "0x0c" + } + }, + + "0000000000000000000000000000000000000000" : { + "balance" : "0", + "code" : "{(SSTORE 0 0)(SSTORE 1 0)(SSTORE 2 0)(SSTORE 3 0)(SSTORE 4 0)(SSTORE 5 0)(SSTORE 6 0)(SSTORE 7 0)(SSTORE 8 0)(SSTORE 9 0)}", + "nonce" : "0", + "storage" : { + "0x" : "0x0c", + "0x01" : "0x0c", + "0x02" : "0x0c", + "0x03" : "0x0c", + "0x04" : "0x0c", + "0x05" : "0x0c", + "0x06" : "0x0c", + "0x07" : "0x0c", + "0x08" : "0x0c", + "0x09" : "0x0c" + } + } + + }, + + "transaction" : + { + "data" : "", + "gasLimit" : "200000", "gasPrice" : "1", "nonce" : "", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", @@ -669,7 +794,7 @@ "c94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { "balance" : "10", - "code" : "{(CALL 20000 0x0000000000000000000000000000000000000000 1 0 0 0 0) (SUICIDE 0)}", + "code" : "{(CALL 22000 0x0000000000000000000000000000000000000000 1 0 0 0 0) (SUICIDE 0)}", "nonce" : "0", "storage" : { } @@ -697,7 +822,7 @@ } }, - "SuicidesAndInternlCallSuicides" : { + "SuicidesAndInternlCallSuicidesSuccess" : { "env" : { "currentCoinbase" : "b94f5374fce5edbc8e2a8697c15331677e6ebf0b", "currentDifficulty" : "45678256", @@ -737,7 +862,7 @@ "transaction" : { "data" : "", - "gasLimit" : "50000", + "gasLimit" : "150000", "gasPrice" : "1", "nonce" : "", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", @@ -767,7 +892,7 @@ "c94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { "balance" : "10000", - "code" : "{(SUICIDE 0) (CALL 2000 0x0000000000000000000000000000000000000000 0 0 0 0 0) }", + "code" : "{(SUICIDE 0) (CALL 30000 0x0000000000000000000000000000000000000000 0 0 0 0 0) }", "nonce" : "0", "storage" : { } @@ -785,7 +910,7 @@ "transaction" : { "data" : "", - "gasLimit" : "33700", + "gasLimit" : "83700", "gasPrice" : "1", "nonce" : "", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", diff --git a/test/ttTransactionTestFiller.json b/test/ttTransactionTestFiller.json index 96e4c78a6..105b64abc 100644 --- a/test/ttTransactionTestFiller.json +++ b/test/ttTransactionTestFiller.json @@ -14,7 +14,7 @@ } }, - "WrongVRSTestVl26" : { + "WrongVRSTestVEqual26" : { "transaction" : { "data" : "", @@ -29,7 +29,7 @@ } }, - "WrongVRSTestVl29" : { + "WrongVRSTestVEqual29" : { "transaction" : { "data" : "", @@ -44,7 +44,7 @@ } }, - "WrongVRSTestVge31" : { + "WrongVRSTestVEqual31" : { "transaction" : { "data" : "", From 3cb340bae6c284b5ce4275b48c42f38e055cb550 Mon Sep 17 00:00:00 2001 From: winsvega Date: Tue, 10 Mar 2015 23:05:59 +0300 Subject: [PATCH 087/100] State Transaction Tests bonus gas at call with value transfer --- test/stTransactionTestFiller.json | 97 ++++++++++++++++++++++++++++++- 1 file changed, 96 insertions(+), 1 deletion(-) diff --git a/test/stTransactionTestFiller.json b/test/stTransactionTestFiller.json index a69f0df27..5d07bf763 100644 --- a/test/stTransactionTestFiller.json +++ b/test/stTransactionTestFiller.json @@ -773,6 +773,102 @@ } }, + "SuicidesAndInternlCallSuicidesBonusGasAtCall" : { + "env" : { + "currentCoinbase" : "b94f5374fce5edbc8e2a8697c15331677e6ebf0b", + "currentDifficulty" : "45678256", + "currentGasLimit" : "1000000", + "currentNumber" : "0", + "currentTimestamp" : 1, + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6" + }, + "pre" : + { + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "100000", + "code" : "", + "nonce" : "0", + "storage" : { + } + }, + + "c94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "10", + "code" : "{(CALL 0 0x0000000000000000000000000000000000000000 1 0 0 0 0) (SUICIDE 0)}", + "nonce" : "0", + "storage" : { + } + }, + + "0000000000000000000000000000000000000000" : { + "balance" : "0", + "code" : "{(SUICIDE 0x0000000000000000000000000000000000000001)}", + "nonce" : "0", + "storage" : { + } + } + }, + + "transaction" : + { + "data" : "", + "gasLimit" : "50000", + "gasPrice" : "1", + "nonce" : "", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "c94f5374fce5edbc8e2a8697c15331677e6ebf0b", + "value" : "10" + } + }, + + "SuicidesAndInternlCallSuicidesBonusGasAtCallFailed" : { + "env" : { + "currentCoinbase" : "b94f5374fce5edbc8e2a8697c15331677e6ebf0b", + "currentDifficulty" : "45678256", + "currentGasLimit" : "1000000", + "currentNumber" : "0", + "currentTimestamp" : 1, + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6" + }, + "pre" : + { + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "100000", + "code" : "", + "nonce" : "0", + "storage" : { + } + }, + + "c94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "10", + "code" : "{(CALL 0 0x0000000000000000000000000000000000000000 0 0 0 0 0) (SUICIDE 0)}", + "nonce" : "0", + "storage" : { + } + }, + + "0000000000000000000000000000000000000000" : { + "balance" : "0", + "code" : "{(SUICIDE 0x0000000000000000000000000000000000000001)}", + "nonce" : "0", + "storage" : { + } + } + }, + + "transaction" : + { + "data" : "", + "gasLimit" : "50000", + "gasPrice" : "1", + "nonce" : "", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "c94f5374fce5edbc8e2a8697c15331677e6ebf0b", + "value" : "10" + } + }, + "SuicidesAndInternlCallSuicidesOOG" : { "env" : { "currentCoinbase" : "b94f5374fce5edbc8e2a8697c15331677e6ebf0b", @@ -807,7 +903,6 @@ "storage" : { } } - }, "transaction" : From 55d83c40fa0a5c6a111c95aaf71209119031891b Mon Sep 17 00:00:00 2001 From: subtly Date: Wed, 11 Mar 2015 14:36:08 +0100 Subject: [PATCH 088/100] fix for -warning in cryptopp.cpp. add mutex to host iteration of peers for connect. only try to connect to as many peers as is necessary. update unit test. --- libdevcrypto/CryptoPP.cpp | 3 +-- libp2p/Host.cpp | 20 ++++++++++++++------ test/peer.cpp | 1 + 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/libdevcrypto/CryptoPP.cpp b/libdevcrypto/CryptoPP.cpp index 11e4c1472..79fd62262 100644 --- a/libdevcrypto/CryptoPP.cpp +++ b/libdevcrypto/CryptoPP.cpp @@ -319,8 +319,7 @@ void Secp256k1::agree(Secret const& _s, Public const& _r, h256& o_s) assert(d.AgreedValueLength() == sizeof(o_s)); byte remote[65] = {0x04}; memcpy(&remote[1], _r.data(), 64); - bool result = d.Agree(o_s.data(), _s.data(), remote); - assert(result); + d.Agree(o_s.data(), _s.data(), remote); } void Secp256k1::exportPublicKey(CryptoPP::DL_PublicKey_EC const& _k, Public& o_p) diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index 8f4f8a69d..19df5ee2e 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -550,14 +550,22 @@ void Host::run(boost::system::error_code const&) // is always live and to ensure reputation and fallback timers are properly // updated. // disconnectLatePeers(); - if (peerCount() < m_idealPeerCount) + auto openSlots = m_idealPeerCount - peerCount(); + if (openSlots > 0) { - for (auto p: m_peers) - if (p.second->shouldReconnect()) - { - connect(p.second); + list> toConnect; + { + RecursiveGuard l(x_sessions); + for (auto p: m_peers) + if (p.second->shouldReconnect()) + toConnect.push_back(p.second); + } + + for (auto p: toConnect) + if (openSlots--) + connect(p); + else break; - } m_nodeTable->discover(); } diff --git a/test/peer.cpp b/test/peer.cpp index 0fe3fd1ed..14712d4f1 100644 --- a/test/peer.cpp +++ b/test/peer.cpp @@ -63,6 +63,7 @@ BOOST_AUTO_TEST_CASE(save_nodes) for (auto i:{0,1,2,3,4,5}) { Host* h = new Host("Test", NetworkPreferences(30300 + i, "127.0.0.1", true, true)); + h->setIdealPeerCount(10); // starting host is required so listenport is available h->start(); while (!h->isStarted()) From c6693142c57e28132b926a00c0d5fcd31b743ac5 Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Wed, 11 Mar 2015 14:41:27 +0100 Subject: [PATCH 089/100] fixed #1273, npm is not used by default --- CMakeLists.txt | 3 ++- cmake/EthDependencies.cmake | 26 ++++++++++++++++++-------- libjsqrc/CMakeLists.txt | 2 +- 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4603bb898..778f7dafe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,6 +22,7 @@ function(createDefaultCacheConfig) set(FATDB OFF CACHE BOOL "Build with ability to list entries in the Trie. Doubles DB size, slows everything down, but good for looking at state diffs and trie contents.") set(JUSTTESTS OFF CACHE BOOL "Build only for tests.") set(SOLIDITY ON CACHE BOOL "Build the Solidity language components (requried unless HEADLESS)") + set(USENPM OFF CACHE BOOL "Use npm to recompile ethereum.js if it was changed") endfunction() @@ -125,7 +126,7 @@ configureProject() set (ETH_HAVE_WEBENGINE 1) message(STATUS "CMAKE_VERSION: ${CMAKE_VERSION}") -message("-- VMTRACE: ${VMTRACE}; PARANOIA: ${PARANOIA}; HEADLESS: ${HEADLESS}; JSONRPC: ${JSONRPC}; EVMJIT: ${EVMJIT}; FATDB: ${FATDB}; CHROMIUM: ${ETH_HAVE_WEBENGINE}") +message("-- VMTRACE: ${VMTRACE}; PARANOIA: ${PARANOIA}; HEADLESS: ${HEADLESS}; JSONRPC: ${JSONRPC}; EVMJIT: ${EVMJIT}; FATDB: ${FATDB}; CHROMIUM: ${ETH_HAVE_WEBENGINE}; USENPM: ${USENPM}") # Default TARGET_PLATFORM to "linux". diff --git a/cmake/EthDependencies.cmake b/cmake/EthDependencies.cmake index 645942998..ea272da56 100644 --- a/cmake/EthDependencies.cmake +++ b/cmake/EthDependencies.cmake @@ -134,14 +134,24 @@ if (NOT HEADLESS) message(" - windeployqt path: ${WINDEPLOYQT_APP}") endif() -# TODO check node && npm version - find_program(ETH_NODE node) - string(REGEX REPLACE "node" "" ETH_NODE_DIRECTORY ${ETH_NODE}) - message(" - nodejs location : ${ETH_NODE}") - - find_program(ETH_NPM npm) - string(REGEX REPLACE "npm" "" ETH_NPM_DIRECTORY ${ETH_NPM}) - message(" - npm location : ${ETH_NPM}") + if (USENPM) + + # TODO check node && npm version + find_program(ETH_NODE node) + string(REGEX REPLACE "node" "" ETH_NODE_DIRECTORY ${ETH_NODE}) + message(" - nodejs location : ${ETH_NODE}") + + find_program(ETH_NPM npm) + string(REGEX REPLACE "npm" "" ETH_NPM_DIRECTORY ${ETH_NPM}) + message(" - npm location : ${ETH_NPM}") + + if (NOT ETH_NODE) + message(FATAL_ERROR "node not found!") + endif() + if (NOT ETH_NPM) + message(FATAL_ERROR "npm not found!") + endif() + endif() endif() #HEADLESS diff --git a/libjsqrc/CMakeLists.txt b/libjsqrc/CMakeLists.txt index 68500ada6..6554e523d 100644 --- a/libjsqrc/CMakeLists.txt +++ b/libjsqrc/CMakeLists.txt @@ -12,7 +12,7 @@ qt5_add_resources(JSQRC js.qrc) add_library(jsqrc STATIC ${JSQRC}) target_link_libraries(jsqrc Qt5::Core) -if (ETH_NODE AND ETH_NPM) +if (USENPM) add_custom_target(ethereumjs) add_custom_command(TARGET ethereumjs POST_BUILD From 150772e778a480359fd6943ee773a5d14ec123d7 Mon Sep 17 00:00:00 2001 From: subtly Date: Wed, 11 Mar 2015 15:44:06 +0100 Subject: [PATCH 090/100] buildbot commit --- libethereum/Client.h | 1 - 1 file changed, 1 deletion(-) diff --git a/libethereum/Client.h b/libethereum/Client.h index 9cbfd7989..2c8db2aed 100644 --- a/libethereum/Client.h +++ b/libethereum/Client.h @@ -46,7 +46,6 @@ namespace dev { - namespace eth { From 772766194c198ed42a7f48ccf503df026864a548 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 11 Mar 2015 15:59:02 +0100 Subject: [PATCH 091/100] Fixed assembly tests. --- test/Assembly.cpp | 54 ++++++++++++++++++++--------------------------- 1 file changed, 23 insertions(+), 31 deletions(-) diff --git a/test/Assembly.cpp b/test/Assembly.cpp index 3869919e7..eb76f1184 100644 --- a/test/Assembly.cpp +++ b/test/Assembly.cpp @@ -74,17 +74,19 @@ eth::AssemblyItems compileContract(const string& _sourceCode) return AssemblyItems(); } -void checkAssemblyLocations(AssemblyItems const& _items, std::vector _locations) +void checkAssemblyLocations(AssemblyItems const& _items, vector const& _locations) { - size_t i = 0; BOOST_CHECK_EQUAL(_items.size(), _locations.size()); - for (auto const& it: _items) + for (size_t i = 0; i < min(_items.size(), _locations.size()); ++i) { - BOOST_CHECK_MESSAGE(it.getLocation() == _locations[i], - std::string("Location mismatch for assembly item ") + std::to_string(i)); - ++i; + BOOST_CHECK_MESSAGE( + _items[i].getLocation() == _locations[i], + "Location mismatch for assembly item " + to_string(i) + ". Found: " + + to_string(_items[i].getLocation().start) + "-" + + to_string(_items[i].getLocation().end) + ", expected: " + + to_string(_locations[i].start) + "-" + + to_string(_locations[i].end)); } - } } // end anonymous namespace @@ -93,31 +95,21 @@ BOOST_AUTO_TEST_SUITE(Assembly) BOOST_AUTO_TEST_CASE(location_test) { - char const* sourceCode = "contract test {\n" - " function f() returns (uint256 a)\n" - " {\n" - " return 16;\n" - " }\n" - "}\n"; - std::shared_ptr n = make_shared("source"); + char const* sourceCode = R"( + contract test { + function f() returns (uint256 a) { + return 16; + } + } + )"; + shared_ptr n = make_shared("source"); AssemblyItems items = compileContract(sourceCode); - std::vector locations { - SourceLocation(0, 77, n), SourceLocation(0, 77, n), - SourceLocation(0, 77, n), SourceLocation(0, 77, n), - SourceLocation(0, 77, n), SourceLocation(0, 77, n), - SourceLocation(0, 77, n), SourceLocation(0, 77, n), - SourceLocation(), SourceLocation(), - SourceLocation(0, 77, n), SourceLocation(0, 77, n), - SourceLocation(), SourceLocation(), SourceLocation(), - SourceLocation(0, 77, n), SourceLocation(0, 77, n), - SourceLocation(0, 77, n), SourceLocation(0, 77, n), - SourceLocation(0, 77, n), SourceLocation(0, 77, n), - SourceLocation(0, 77, n), - SourceLocation(18, 75, n), SourceLocation(40, 49, n), - SourceLocation(61, 70, n), SourceLocation(61, 70, n), SourceLocation(61, 70, n), - SourceLocation(), SourceLocation(), - SourceLocation(61, 70, n), SourceLocation(61, 70, n), SourceLocation(61, 70, n) - }; + vector locations = + vector(11, SourceLocation(2, 75, n)) + + vector(12, SourceLocation(20, 72, n)) + + vector{SourceLocation(42, 51, n), SourceLocation(65, 67, n)} + + vector(4, SourceLocation(58, 67, n)) + + vector(3, SourceLocation(20, 72, n)); checkAssemblyLocations(items, locations); } From 4ec057611a5c07bd0004ccb4573b87e93788e1ef Mon Sep 17 00:00:00 2001 From: winsvega Date: Wed, 11 Mar 2015 18:22:11 +0300 Subject: [PATCH 092/100] State Tests POC9 updates --- test/stBlockHashTestFiller.json | 18 ++-- test/stInitCodeTestFiller.json | 41 ++++++++ test/stMemoryTestFiller.json | 2 +- test/stPreCompiledContractsFiller.json | 70 +++++++++---- test/stSolidityTestFiller.json | 133 ++++++++++++++++++++----- test/stTransactionTestFiller.json | 1 + 6 files changed, 213 insertions(+), 52 deletions(-) diff --git a/test/stBlockHashTestFiller.json b/test/stBlockHashTestFiller.json index 5ecc5b1c7..32745e89b 100644 --- a/test/stBlockHashTestFiller.json +++ b/test/stBlockHashTestFiller.json @@ -3,7 +3,7 @@ "env" : { "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", "currentNumber" : "5", - "currentGasLimit" : "1000000", + "currentGasLimit" : "100000000000", "currentDifficulty" : "256", "currentTimestamp" : 1, "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" @@ -16,7 +16,7 @@ "storage": {} }, "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { - "balance" : "100000", + "balance" : "1000000", "nonce" : "0", "code" : "", "storage": {} @@ -25,7 +25,7 @@ "transaction" : { "nonce" : "0", "gasPrice" : "1", - "gasLimit" : "28500", + "gasLimit" : "285000", "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", "value" : "10", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", @@ -37,7 +37,7 @@ "env" : { "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", "currentNumber" : "257", - "currentGasLimit" : "1000000", + "currentGasLimit" : "100000000", "currentDifficulty" : "256", "currentTimestamp" : 1, "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" @@ -50,7 +50,7 @@ "storage": {} }, "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { - "balance" : "100000", + "balance" : "1000000", "nonce" : "0", "code" : "", "storage": {} @@ -59,7 +59,7 @@ "transaction" : { "nonce" : "0", "gasPrice" : "1", - "gasLimit" : "28500", + "gasLimit" : "285000", "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", "value" : "10", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", @@ -71,7 +71,7 @@ "env" : { "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", "currentNumber" : "257", - "currentGasLimit" : "1000000", + "currentGasLimit" : "100000000", "currentDifficulty" : "256", "currentTimestamp" : 1, "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" @@ -84,7 +84,7 @@ "storage": {} }, "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { - "balance" : "100000", + "balance" : "1000000", "nonce" : "0", "code" : "", "storage": {} @@ -93,7 +93,7 @@ "transaction" : { "nonce" : "0", "gasPrice" : "1", - "gasLimit" : "28500", + "gasLimit" : "285000", "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", "value" : "10", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", diff --git a/test/stInitCodeTestFiller.json b/test/stInitCodeTestFiller.json index cb1b4d14a..2c155468f 100644 --- a/test/stInitCodeTestFiller.json +++ b/test/stInitCodeTestFiller.json @@ -371,6 +371,46 @@ } }, + "CallContractToCreateContractOOGBonusGas" : { + "env" : { + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "currentDifficulty" : "45678256", + "currentGasLimit" : "100000000", + "currentNumber" : "0", + "currentTimestamp" : 1, + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6" + }, + "pre" : + { + "095e7baea6a6c7c4c2dfeb977efac326af552d87": { + "balance": "112", + "nonce": "0", + "//": "(CREATE 0 64 32)", + "//": "{[[0]] 12 (MSTORE 32 0x602060406000f0)(RETURN 57 7)}", + "code": "{(MSTORE 0 0x600c60005566602060406000f060205260076039f3)[[0]](CREATE 1 11 21)(CALL 0 (SLOAD 0) 12 0 0 0 0)}", + "storage": {} + }, + + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "100000000", + "code" : "", + "nonce" : "0", + "storage" : { + } + } + }, + "transaction" : + { + "data" : "0x00", + "gasLimit" : "20000000", + "gasPrice" : "1", + "nonce" : "0", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "0" + } + }, + "CallContractToCreateContractOOG" : { "env" : { "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", @@ -385,6 +425,7 @@ "095e7baea6a6c7c4c2dfeb977efac326af552d87": { "balance": "0", "nonce": "0", + "//": "Create should fail. So CALL goes to 0x0...0", "//": "(CREATE 0 64 32)", "//": "{[[0]] 12 (MSTORE 32 0x602060406000f0)(RETURN 57 7)}", "code": "{(MSTORE 0 0x600c60005566602060406000f060205260076039f3)[[0]](CREATE 1 11 21)(CALL 0 (SLOAD 0) 0 0 0 0 0)}", diff --git a/test/stMemoryTestFiller.json b/test/stMemoryTestFiller.json index f34307443..676a88feb 100644 --- a/test/stMemoryTestFiller.json +++ b/test/stMemoryTestFiller.json @@ -25,7 +25,7 @@ "transaction" : { "nonce" : "0", "gasPrice" : "1", - "gasLimit" : "22000", + "gasLimit" : "220000", "to" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", "value" : "10", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", diff --git a/test/stPreCompiledContractsFiller.json b/test/stPreCompiledContractsFiller.json index d571dfccc..9e5fa862a 100644 --- a/test/stPreCompiledContractsFiller.json +++ b/test/stPreCompiledContractsFiller.json @@ -12,7 +12,7 @@ "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { "balance" : "20000000", "nonce" : "0", - "code": "{ (MSTORE 0 0x18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c) (MSTORE 32 28) (MSTORE 64 0x73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75f) (MSTORE 96 0xeeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549) [[ 2 ]] (CALL 1000 1 0 0 128 128 32) [[ 0 ]] (MOD (MLOAD 128) (EXP 2 160)) [[ 1 ]] (EQ (ORIGIN) (SLOAD 0)) }", + "code": "{ (MSTORE 0 0x18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c) (MSTORE 32 28) (MSTORE 64 0x73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75f) (MSTORE 96 0xeeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549) [[ 2 ]] (CALL 100000 1 0 0 128 128 32) [[ 0 ]] (MOD (MLOAD 128) (EXP 2 160)) [[ 1 ]] (EQ (ORIGIN) (SLOAD 0)) }", "storage": {} }, "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { @@ -25,7 +25,7 @@ "transaction" : { "nonce" : "0", "gasPrice" : "1", - "gasLimit" : "365224", + "gasLimit" : "3652240", "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", "value" : "100000", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", @@ -46,7 +46,7 @@ "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { "balance" : "20000000", "nonce" : "0", - "code": "{ (MSTORE 0 0x18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c) (MSTORE 32 28) (MSTORE 64 0x73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75f) (MSTORE 96 0xeeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549) [[ 2 ]] (CALL 1000 1 0 0 128 64 32) [[ 0 ]] (MOD (MLOAD 64) (EXP 2 160)) [[ 1 ]] (EQ (ORIGIN) (SLOAD 0)) }", + "code": "{ (MSTORE 0 0x18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c) (MSTORE 32 28) (MSTORE 64 0x73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75f) (MSTORE 96 0xeeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549) [[ 2 ]] (CALL 100000 1 0 0 128 64 32) [[ 0 ]] (MOD (MLOAD 64) (EXP 2 160)) [[ 1 ]] (EQ (ORIGIN) (SLOAD 0)) }", "storage": {} }, "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { @@ -81,7 +81,41 @@ "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { "balance" : "20000000", "nonce" : "0", - "code": "{ (MSTORE 0 0x18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c) (MSTORE 32 28) (MSTORE 64 0x73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75f) (MSTORE 96 0xeeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549) [[ 2 ]] (CALL 1000 1 0 0 128 128 32) [[ 0 ]] (MLOAD 128) }", + "code": "{ (MSTORE 0 0x18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c) (MSTORE 32 28) (MSTORE 64 0x73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75f) (MSTORE 96 0xeeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549) [[ 2 ]] (CALL 100000 1 0 0 128 128 32) [[ 0 ]] (MLOAD 128) }", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000000000000000", + "nonce" : "0", + "code" : "", + "storage": {} + } + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "365224", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "100000", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + + "CallEcrecover0_gas3000": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "10000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "20000000", + "nonce" : "0", + "code": "{ (MSTORE 0 0x18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c) (MSTORE 32 28) (MSTORE 64 0x73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75f) (MSTORE 96 0xeeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549) [[ 2 ]] (CALL 3000 1 0 0 128 128 32) [[ 0 ]] (MOD (MLOAD 128) (EXP 2 160)) [[ 1 ]] (EQ (ORIGIN) (SLOAD 0)) }", "storage": {} }, "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { @@ -102,7 +136,7 @@ } }, - "CallEcrecover0_gas500": { + "CallEcrecover0_BonusGas": { "env" : { "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", "currentNumber" : "0", @@ -115,7 +149,7 @@ "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { "balance" : "20000000", "nonce" : "0", - "code": "{ (MSTORE 0 0x18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c) (MSTORE 32 28) (MSTORE 64 0x73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75f) (MSTORE 96 0xeeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549) [[ 2 ]] (CALL 500 1 0 0 128 128 32) [[ 0 ]] (MOD (MLOAD 128) (EXP 2 160)) [[ 1 ]] (EQ (ORIGIN) (SLOAD 0)) }", + "code": "{ (MSTORE 0 0x18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c) (MSTORE 32 28) (MSTORE 64 0x73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75f) (MSTORE 96 0xeeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549) [[ 2 ]] (CALL 0 1 1 0 128 128 32) [[ 0 ]] (MOD (MLOAD 128) (EXP 2 160)) [[ 1 ]] (EQ (ORIGIN) (SLOAD 0)) }", "storage": {} }, "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { @@ -136,7 +170,7 @@ } }, - "CallEcrecover0_Gas499": { + "CallEcrecover0_Gas2999": { "env" : { "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", "currentNumber" : "0", @@ -149,7 +183,7 @@ "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { "balance" : "20000000", "nonce" : "0", - "code": "{ (MSTORE 0 0x18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c) (MSTORE 32 28) (MSTORE 64 0x73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75f) (MSTORE 96 0xeeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549) [[ 2 ]] (CALL 499 1 0 0 128 128 32) [[ 0 ]] (MOD (MLOAD 128) (EXP 2 160)) [[ 1 ]] (EQ (ORIGIN) (SLOAD 0)) }", + "code": "{ (MSTORE 0 0x18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c) (MSTORE 32 28) (MSTORE 64 0x73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75f) (MSTORE 96 0xeeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549) [[ 2 ]] (CALL 2999 1 0 0 128 128 32) [[ 0 ]] (MOD (MLOAD 128) (EXP 2 160)) [[ 1 ]] (EQ (ORIGIN) (SLOAD 0)) }", "storage": {} }, "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { @@ -183,7 +217,7 @@ "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { "balance" : "20000000", "nonce" : "0", - "code": "{ [[ 2 ]] (CALL 1000 1 0 0 128 128 32) [[ 0 ]] (MOD (MLOAD 128) (EXP 2 160)) }", + "code": "{ [[ 2 ]] (CALL 100000 1 0 0 128 128 32) [[ 0 ]] (MOD (MLOAD 128) (EXP 2 160)) }", "storage": {} }, "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { @@ -217,7 +251,7 @@ "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { "balance" : "20000000", "nonce" : "0", - "code": "{ (MSTORE 0 0x18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c) (MSTORE 32 1) (MSTORE 64 0x73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75f) (MSTORE 96 0xeeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549) [[ 2 ]] (CALL 1000 1 0 0 128 128 32) [[ 0 ]] (MOD (MLOAD 128) (EXP 2 160)) [[ 1 ]] (EQ (ORIGIN) (SLOAD 0)) }", + "code": "{ (MSTORE 0 0x18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c) (MSTORE 32 1) (MSTORE 64 0x73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75f) (MSTORE 96 0xeeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549) [[ 2 ]] (CALL 100000 1 0 0 128 128 32) [[ 0 ]] (MOD (MLOAD 128) (EXP 2 160)) [[ 1 ]] (EQ (ORIGIN) (SLOAD 0)) }", "storage": {} }, "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { @@ -251,7 +285,7 @@ "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { "balance" : "20000000", "nonce" : "0", - "code": "{ (MSTORE 0 0x18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c) (MSTORE 32 28) (MSTORE 33 0x73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75f) (MSTORE 65 0xeeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549) [[ 2 ]] (CALL 1000 1 0 0 97 97 32) [[ 0 ]] (MOD (MLOAD 97) (EXP 2 160)) [[ 1 ]] (EQ (ORIGIN) (SLOAD 0)) }", + "code": "{ (MSTORE 0 0x18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c) (MSTORE 32 28) (MSTORE 33 0x73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75f) (MSTORE 65 0xeeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549) [[ 2 ]] (CALL 100000 1 0 0 97 97 32) [[ 0 ]] (MOD (MLOAD 97) (EXP 2 160)) [[ 1 ]] (EQ (ORIGIN) (SLOAD 0)) }", "storage": {} }, "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { @@ -285,7 +319,7 @@ "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { "balance" : "20000000", "nonce" : "0", - "code": "{ (MSTORE 0 0x2f380a2dea7e778d81affc2443403b8fe4644db442ae4862ff5bb3732829cdb9) (MSTORE 32 27) (MSTORE 64 0x6b65ccb0558806e9b097f27a396d08f964e37b8b7af6ceeb516ff86739fbea0a) (MSTORE 96 0x37cbc8d883e129a4b1ef9d5f1df53c4f21a3ef147cf2a50a4ede0eb06ce092d4) [[ 2 ]] (CALL 1000 1 0 0 128 128 32) [[ 0 ]] (MOD (MLOAD 128) (EXP 2 160)) [[ 1 ]] (EQ (ORIGIN) (SLOAD 0)) }", + "code": "{ (MSTORE 0 0x2f380a2dea7e778d81affc2443403b8fe4644db442ae4862ff5bb3732829cdb9) (MSTORE 32 27) (MSTORE 64 0x6b65ccb0558806e9b097f27a396d08f964e37b8b7af6ceeb516ff86739fbea0a) (MSTORE 96 0x37cbc8d883e129a4b1ef9d5f1df53c4f21a3ef147cf2a50a4ede0eb06ce092d4) [[ 2 ]] (CALL 100000 1 0 0 128 128 32) [[ 0 ]] (MOD (MLOAD 128) (EXP 2 160)) [[ 1 ]] (EQ (ORIGIN) (SLOAD 0)) }", "storage": {} }, "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { @@ -625,7 +659,7 @@ "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { "balance" : "20000000", "nonce" : "0", - "code" : "{ [[ 2 ]] (CALL 500 3 0 0 0 0 32) [[ 0 ]] (MLOAD 0)}", + "code" : "{ [[ 2 ]] (CALL 600 3 0 0 0 0 32) [[ 0 ]] (MLOAD 0)}", "storage": {} }, "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { @@ -659,7 +693,7 @@ "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { "balance" : "20000000", "nonce" : "0", - "code" : "{ (MSTORE 5 0xf34578907f) [[ 2 ]] (CALL 500 3 0 0 37 0 32) [[ 0 ]] (MLOAD 0)}", + "code" : "{ (MSTORE 5 0xf34578907f) [[ 2 ]] (CALL 600 3 0 0 37 0 32) [[ 0 ]] (MLOAD 0)}", "storage": {} }, "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { @@ -693,7 +727,7 @@ "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { "balance" : "20000000", "nonce" : "0", - "code" : "{ (MSTORE 0 0xf34578907f) [[ 2 ]] (CALL 500 3 0 0 37 0 32) [[ 0 ]] (MLOAD 0)}", + "code" : "{ (MSTORE 0 0xf34578907f) [[ 2 ]] (CALL 600 3 0 0 37 0 32) [[ 0 ]] (MLOAD 0)}", "storage": {} }, "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { @@ -727,7 +761,7 @@ "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { "balance" : "20000000", "nonce" : "0", - "code" : "{ (MSTORE 0 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) [[ 2 ]] (CALL 100 3 0 0 32 0 32) [[ 0 ]] (MLOAD 0)}", + "code" : "{ (MSTORE 0 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) [[ 2 ]] (CALL 120 3 0 0 32 0 32) [[ 0 ]] (MLOAD 0)}", "storage": {} }, "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { @@ -761,7 +795,7 @@ "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { "balance" : "20000000", "nonce" : "0", - "code" : "{ (MSTORE 0 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) [[ 2 ]] (CALL 99 3 0 0 32 0 32) [[ 0 ]] (MLOAD 0)}", + "code" : "{ (MSTORE 0 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) [[ 2 ]] (CALL 119 3 0 0 32 0 32) [[ 0 ]] (MLOAD 0)}", "storage": {} }, "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { @@ -795,7 +829,7 @@ "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { "balance" : "20000000", "nonce" : "0", - "code" : "{ (MSTORE 0 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) [[ 2 ]] (CALL 500 3 0 0 1000000 0 32) [[ 0 ]] (MLOAD 0)}", + "code" : "{ (MSTORE 0 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) [[ 2 ]] (CALL 600 3 0 0 1000000 0 32) [[ 0 ]] (MLOAD 0)}", "storage": {} }, "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { diff --git a/test/stSolidityTestFiller.json b/test/stSolidityTestFiller.json index 948dc5f7a..f2a2a0aa0 100644 --- a/test/stSolidityTestFiller.json +++ b/test/stSolidityTestFiller.json @@ -3,7 +3,7 @@ "env" : { "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", "currentDifficulty" : "45678256", - "currentGasLimit" : "1000000000000000000000", + "currentGasLimit" : "1000000000000000000000000", "currentNumber" : "120", "currentTimestamp" : 1, "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6" @@ -11,7 +11,7 @@ "pre" : { "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { - "balance" : "10000000000000000", + "balance" : "1000000000000000000", "code" : "", "nonce" : "0", "storage" : { @@ -19,7 +19,7 @@ }, "d94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { - "balance" : "100000", + "balance" : "1000000", "//" : " ", "//" : "contract TestContract ", "//" : "{ ", @@ -203,7 +203,7 @@ "//" : " a = new TestContract(); ", "//" : " } ", "//" : "} ", - "code" : "0x60003560e060020a900480630c4c9a8014610084578063296df0df146100965780632a9afb83146100a8578063380e4396146100ba5780634893d88a146100cc5780637ee17e12146100da578063981a3165146100e8578063a60eedda146100fa578063e0a9fd281461010c578063e97384dc1461011e578063ed973fe91461013057005b61008c6102c0565b8060005260206000f35b61009e61067b565b8060005260206000f35b6100b06101ba565b8060005260206000f35b6100c261049b565b8060005260206000f35b6100d461087d565b60006000f35b6100e26101a4565b60006000f35b6100f06102ab565b8060005260206000f35b610102610695565b8060005260206000f35b610114610732565b8060005260206000f35b61012661055a565b8060005260206000f35b610138610142565b8060005260206000f35b600060006060610889600039606060006000f0905080600160a060020a031663b9c3d0a5602060008260e060020a026000526004600060008660155a03f150505060005160e1146101925761019b565b600191506101a0565b600091505b5090565b60006060610889600039606060006000f0905090565b60006001905060005460ff14156101d0576101d9565b600090506102a8565b60025460005414156101ea576101f3565b600090506102a8565b600154600160a060020a031673a94f5374fce5edbc8e2a8697c15331677e6ebf0b141561021f57610228565b600090506102a8565b6003547f676c6f62616c2064617461203332206c656e67746820737472696e6700000000141561025757610260565b600090506102a8565b600460006000815260200190815260200160002054600160a060020a031673a94f5374fce5edbc8e2a8697c15331677e6ebf0b141561029e576102a7565b600090506102a8565b5b90565b6000600090506102b961087d565b6001905090565b6000600090506102ce6101a4565b506102d761049b565b156102e157610307565b7ff000000000000000000000000000000000000000000000000000000000000000810190505b61030f610142565b156103195761033f565b7f0f00000000000000000000000000000000000000000000000000000000000000810190505b610347610695565b1561035157610376565b7ef0000000000000000000000000000000000000000000000000000000000000810190505b61037e61055a565b15610388576103ad565b7e0f000000000000000000000000000000000000000000000000000000000000810190505b60ff60008190555073a94f5374fce5edbc8e2a8697c15331677e6ebf0b60018190555060ff6002819055507f676c6f62616c2064617461203332206c656e67746820737472696e670000000060038190555073a94f5374fce5edbc8e2a8697c15331677e6ebf0b6004600060008152602001908152602001600020819055506104346101ba565b1561043e57610462565b7df00000000000000000000000000000000000000000000000000000000000810190505b61046a610732565b1561047457610498565b7d0f0000000000000000000000000000000000000000000000000000000000810190505b90565b6000600060006000915060009250816000146104b65761053a565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7821315610530575b600a8212156104f55781806001019250506104df565b81600a146105025761052b565b600a90505b60008160ff16111561052a57818060019003925050808060019003915050610507565b5b610539565b60009250610555565b5b8160001461054757610550565b60019250610555565b600092505b505090565b60006001905041600160a060020a0316732adc25665018aa1fe0e6bc666dac8fc2697ff9ba141561058a57610593565b60009050610678565b446302b8feb014156105a4576105ad565b60009050610678565b45683635c9adc5dea0000014156105c3576105cc565b60009050610678565b43607814156105da576105e3565b60009050610678565b33600160a060020a031673a94f5374fce5edbc8e2a8697c15331677e6ebf0b141561060d57610616565b60009050610678565b34606414156106245761062d565b60009050610678565b3a6001141561063b57610644565b60009050610678565b32600160a060020a031673a94f5374fce5edbc8e2a8697c15331677e6ebf0b141561066e57610677565b60009050610678565b5b90565b6000600090505b60011561068e57610682565b6001905090565b60006000600191506060610889600039606060006000f0905080600160a060020a031662f55d9d600060008260e060020a02600052600441600160a060020a03168152602001600060008660155a03f150505080600160a060020a031663b9c3d0a5602060008260e060020a026000526004600060008660155a03f150505060005160e114156107245761072d565b6000915061072e565b5b5090565b60006001905060007f74657374737472696e67000000000000000000000000000000000000000000008152600a016000207f43c4b4524adb81e4e9a5c4648a98e9d320e3908ac5b6c889144b642cd08ae16d141561078f57610798565b6000905061087a565b60026020600060007f74657374737472696e67000000000000000000000000000000000000000000008152600a01600060008560155a03f150506000517f3c8727e019a42b444667a587b6001251becadabbb36bfed8087a92c18882d11114156108015761080a565b6000905061087a565b60036020600060007f74657374737472696e67000000000000000000000000000000000000000000008152600a01600060008560155a03f15050600051600160a060020a031673cd566972b5e50104011a92b59fa8e0b1234851ae141561087057610879565b6000905061087a565b5b90565b6108856102ab565b505600605480600c6000396000f30060003560e060020a90048062f55d9d14601e578063b9c3d0a514602d57005b60276004356046565b60006000f35b6033603d565b8060005260206000f35b600060e1905090565b80600160a060020a0316ff5056", + "code" : "0x60003560e060020a900480630c4c9a80146100635780632a9afb8314610075578063380e4396146100875780637ee17e1214610099578063a60eedda146100a7578063e0a9fd28146100b9578063e97384dc146100cb578063ed973fe9146100dd57005b61006b610473565b8060005260206000f35b61007d61064e565b8060005260206000f35b61008f61073f565b8060005260206000f35b6100a16107fe565b60006000f35b6100af6100ef565b8060005260206000f35b6100c1610196565b8060005260206000f35b6100d3610352565b8060005260206000f35b6100e56102eb565b8060005260206000f35b60006000600191506060610815600039606060006000f0905080600160a060020a031662f55d9d600060008260e060020a02600052600441600160a060020a03168152602001600060008660325a03f161014557005b505080600160a060020a031663b9c3d0a5602060008260e060020a026000526004600060008660325a03f161017657005b505060005160e1141561018857610191565b60009150610192565b5b5090565b60006001905060007f74657374737472696e67000000000000000000000000000000000000000000008152600a016000207f43c4b4524adb81e4e9a5c4648a98e9d320e3908ac5b6c889144b642cd08ae16d14156101f3576101fc565b600090506102e8565b60026020600060007f74657374737472696e67000000000000000000000000000000000000000000008152600a01600060008560325a03f161023a57005b506000517f3c8727e019a42b444667a587b6001251becadabbb36bfed8087a92c18882d111141561026a57610273565b600090506102e8565b60036020600060007f74657374737472696e67000000000000000000000000000000000000000000008152600a01600060008560325a03f16102b157005b50600051600160a060020a031673cd566972b5e50104011a92b59fa8e0b1234851ae14156102de576102e7565b600090506102e8565b5b90565b600060006060610815600039606060006000f0905080600160a060020a031663b9c3d0a5602060008260e060020a026000526004600060008660325a03f161032f57005b505060005160e11461034057610349565b6001915061034e565b600091505b5090565b60006001905041600160a060020a0316732adc25665018aa1fe0e6bc666dac8fc2697ff9ba14156103825761038b565b60009050610470565b446302b8feb0141561039c576103a5565b60009050610470565b45683635c9adc5dea0000014156103bb576103c4565b60009050610470565b43607814156103d2576103db565b60009050610470565b33600160a060020a031673a94f5374fce5edbc8e2a8697c15331677e6ebf0b14156104055761040e565b60009050610470565b346064141561041c57610425565b60009050610470565b3a600114156104335761043c565b60009050610470565b32600160a060020a031673a94f5374fce5edbc8e2a8697c15331677e6ebf0b14156104665761046f565b60009050610470565b5b90565b6000600090506104816107fe565b5061048a61073f565b15610494576104ba565b7ff000000000000000000000000000000000000000000000000000000000000000810190505b6104c26102eb565b156104cc576104f2565b7f0f00000000000000000000000000000000000000000000000000000000000000810190505b6104fa6100ef565b1561050457610529565b7ef0000000000000000000000000000000000000000000000000000000000000810190505b610531610352565b1561053b57610560565b7e0f000000000000000000000000000000000000000000000000000000000000810190505b60ff60008190555073a94f5374fce5edbc8e2a8697c15331677e6ebf0b60018190555060ff6002819055507f676c6f62616c2064617461203332206c656e67746820737472696e670000000060038190555073a94f5374fce5edbc8e2a8697c15331677e6ebf0b6004600060008152602001908152602001600020819055506105e761064e565b156105f157610615565b7df00000000000000000000000000000000000000000000000000000000000810190505b61061d610196565b156106275761064b565b7d0f0000000000000000000000000000000000000000000000000000000000810190505b90565b60006001905060005460ff14156106645761066d565b6000905061073c565b600254600054141561067e57610687565b6000905061073c565b600154600160a060020a031673a94f5374fce5edbc8e2a8697c15331677e6ebf0b14156106b3576106bc565b6000905061073c565b6003547f676c6f62616c2064617461203332206c656e67746820737472696e670000000014156106eb576106f4565b6000905061073c565b600460006000815260200190815260200160002054600160a060020a031673a94f5374fce5edbc8e2a8697c15331677e6ebf0b14156107325761073b565b6000905061073c565b5b90565b60006000600060009150600092508160001461075a576107de565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe78213156107d4575b600a821215610799578180600101925050610783565b81600a146107a6576107cf565b600a90505b60008160ff1611156107ce578180600190039250508080600190039150506107ab565b5b6107dd565b600092506107f9565b5b816000146107eb576107f4565b600192506107f9565b600092505b505090565b60006060610815600039606060006000f09050905600605480600c6000396000f30060003560e060020a90048062f55d9d14601e578063b9c3d0a514602d57005b60276004356046565b60006000f35b6033603d565b8060005260206000f35b600060e1905090565b80600160a060020a0316ff5056", "nonce" : "0", "storage" : { } @@ -216,7 +216,7 @@ "data" : "0x7ee17e12", "//" : "runSolidityTests()", "data" : "0x0c4c9a80", - "gasLimit" : "465224", + "gasLimit" : "15000000", "gasPrice" : "1", "nonce" : "0", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", @@ -249,11 +249,13 @@ "//": "contract main ", "//": "{ ", "//": " uint data; ", + "//": " address msgsender; ", "//": " function run() returns (uint) ", "//": " { ", "//": " data = 1; ", + "//": " msgsender = 0x095e7baea6a6c7c4c2dfeb977efac326af552d87; ", "//": " subcaller a = new subcaller(); ", - "//": " a.init(msg.sender); ", + "//": " a.init(msgsender); ", "//": " return data; ", "//": " } ", "//": " ", @@ -262,13 +264,13 @@ "//": " data = _data; ", "//": " } ", "//": "}", - "code" : "0x60e060020a60003504806330debb4214610020578063c04062261461003157005b61002b6004356100a4565b60006000f35b610039610043565b8060005260206000f35b60006000600160008190555060656100af600039606560006000f0905080600160a060020a03166319ab453c600060008260e060020a02600052600433600160a060020a03168152602001600060008660155a03f150505060005491505090565b80600081905550505600605980600c6000396000f30060e060020a60003504806319ab453c14601457005b601d6004356023565b60006000f35b80600160a060020a03166330debb42600060008260e060020a02600052600460e18152602001600060008660155a03f15050505056", + "code" : "0x60003560e060020a9004806330debb4214610021578063c04062261461003257005b61002c6004356100c7565b60006000f35b61003a610044565b8060005260206000f35b60006000600160008190555073095e7baea6a6c7c4c2dfeb977efac326af552d87600181905550606a6100d2600039606a60006000f0905080600160a060020a03166319ab453c600060008260e060020a026000526004600154600160a060020a03168152602001600060008660325a03f16100bc57005b505060005491505090565b80600081905550505600605e80600c6000396000f30060003560e060020a9004806319ab453c14601557005b601e6004356024565b60006000f35b80600160a060020a03166330debb42600060008260e060020a02600052600460e18152602001600060008660325a03f1605957005b50505056", "nonce" : "0", "storage" : { } }, "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { - "balance" : "100000", + "balance" : "1000000", "nonce" : "0", "code" : "", "storage": {} @@ -278,7 +280,7 @@ { "//" : "run()", "data" : "0xc0406226", - "gasLimit" : "35000", + "gasLimit" : "350000", "gasPrice" : "1", "nonce" : "0", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", @@ -287,7 +289,7 @@ } }, - "CallRecursiveMethods" : { + "CallRecursiveMethods" : { "env" : { "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", "currentDifficulty" : "45678256", @@ -317,13 +319,13 @@ "//" : " testRecursiveMethods(); ", "//" : " } ", "//" : "}", - "code" : "0x60e060020a600035048063296df0df1460285780634893d88a146034578063981a316514604057005b602e604c565b60006000f35b603a6061565b60006000f35b60466059565b60006000f35b5b600115605757604d565b565b605f6061565b565b60676059565b56", + "code" : "0x60003560e060020a90048063296df0df1460295780634893d88a146035578063981a316514604157005b602f604d565b60006000f35b603b6062565b60006000f35b6047605a565b60006000f35b5b600115605857604e565b565b60606062565b565b6068605a565b56", "nonce" : "0", "storage" : { } }, "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { - "balance" : "100000", + "balance" : "500000", "nonce" : "0", "code" : "", "storage": {} @@ -333,7 +335,7 @@ { "//" : "testRecursiveMethods()", "data" : "0x981a3165", - "gasLimit" : "25000", + "gasLimit" : "30000", "gasPrice" : "1", "nonce" : "0", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", @@ -372,13 +374,13 @@ "//" : " testRecursiveMethods(); ", "//" : " } ", "//" : "}", - "code" : "0x60e060020a600035048063296df0df1460285780634893d88a146034578063981a316514604057005b602e604c565b60006000f35b603a6061565b60006000f35b60466059565b60006000f35b5b600115605757604d565b565b605f6061565b565b60676059565b56", + "code" : "0x60003560e060020a90048063296df0df1460295780634893d88a146035578063981a316514604157005b602f604d565b60006000f35b603b6062565b60006000f35b6047605a565b60006000f35b5b600115605857604e565b565b60606062565b565b6068605a565b56", "nonce" : "0", "storage" : { } }, "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { - "balance" : "50000", + "balance" : "500000", "nonce" : "0", "code" : "", "storage": {} @@ -388,7 +390,86 @@ { "//" : "testInfiniteLoop()", "data" : "0x296df0df", - "gasLimit" : "30000", + "gasLimit" : "300000", + "gasPrice" : "1", + "nonce" : "0", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "1" + } + }, + + "RecursiveCreateContractsCreate4Contracts" : { + "env" : { + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "currentDifficulty" : "45678256", + "currentGasLimit" : "100000000", + "currentNumber" : "0", + "currentTimestamp" : 1, + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6" + }, + "pre" : + { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "1000000000000000000000000000000000", + "//" : "contract recursiveCreate1 ", + "//" : "{ ", + "//" : " uint depp; ", + "//" : " function recursiveCreate1(address a, uint depth) ", + "//" : " { ", + "//" : " depth = depth - 1; ", + "//" : " depp = depth; ", + "//" : " if(depth > 0) ", + "//" : " main(a).create2(depth); ", + "//" : " } ", + "//" : "} ", + "//" : " ", + "//" : "contract recursiveCreate2 ", + "//" : "{ ", + "//" : " uint depp; ", + "//" : " function recursiveCreate2(address a, uint depth) ", + "//" : " { ", + "//" : " depth = depth - 1; ", + "//" : " depp = depth; ", + "//" : " if(depth > 0) ", + "//" : " recursiveCreate1 rec1 = new recursiveCreate1(a, depth); ", + "//" : " } ", + "//" : "} ", + "//" : " ", + "//" : "contract main ", + "//" : "{ ", + "//" : " address maincontract; ", + "//" : " uint depp; ", + "//" : " function run(uint depth) ", + "//" : " { ", + "//" : " maincontract = 0x095e7baea6a6c7c4c2dfeb977efac326af552d87; ", + "//" : " depp = depth; ", + "//" : " recursiveCreate1 rec1 = new recursiveCreate1(maincontract, depth); ", + "//" : " } ", + "//" : " ", + "//" : " function create2(uint depth) ", + "//" : " { ", + "//" : " recursiveCreate2 rec2 = new recursiveCreate2(maincontract, depth); ", + "//" : " address(rec2).send(2); ", + "//" : " } ", + "//" : "}", + "code" : "0x60003560e060020a90048063820b13f614610021578063a444f5e91461003257005b61002c600435610093565b60006000f35b61003d600435610043565b60006000f35b600073095e7baea6a6c7c4c2dfeb977efac326af552d8760008190555081600181905550606b6101ad600039606b600054600160a060020a0316815260200182815260200160006000f090505050565b600060c86100e560003960c8600054600160a060020a0316815260200182815260200160006000f0905080600160a060020a0316600060026000600060006000848787f16100dd57005b50505050505600604060c860043960045160245160006001820391508160008190555060008211602657604c565b606b605d600039606b83600160a060020a0316815260200182815260200160006000f090505b505050600180605c6000396000f300006040606b6004396004516024516001810390508060008190555060008111602457605b565b81600160a060020a031663820b13f6600060008260e060020a026000526004858152602001600060008660325a03f1605857005b50505b5050600180606a6000396000f300006040606b6004396004516024516001810390508060008190555060008111602457605b565b81600160a060020a031663820b13f6600060008260e060020a026000526004858152602001600060008660325a03f1605857005b50505b5050600180606a6000396000f30000", + "nonce" : "0", + "storage" : { + } + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "5000000", + "nonce" : "0", + "code" : "", + "storage": {} + } + }, + "transaction" : + { + "//" : "run(uint256)", + "data" : "0xa444f5e90000000000000000000000000000000000000000000000000000000000000004", + "gasLimit" : "300000", "gasPrice" : "1", "nonce" : "0", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", @@ -409,12 +490,14 @@ "pre" : { "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { - "balance" : "1000000", + "balance" : "1000000000000000000000000000000000", "//" : "contract recursiveCreate1 ", "//" : "{ ", + "//" : " uint depp; ", "//" : " function recursiveCreate1(address a, uint depth) ", "//" : " { ", "//" : " depth = depth - 1; ", + "//" : " depp = depth; ", "//" : " if(depth > 0) ", "//" : " main(a).create2(depth); ", "//" : " } ", @@ -422,9 +505,11 @@ "//" : " ", "//" : "contract recursiveCreate2 ", "//" : "{ ", + "//" : " uint depp; ", "//" : " function recursiveCreate2(address a, uint depth) ", "//" : " { ", "//" : " depth = depth - 1; ", + "//" : " depp = depth; ", "//" : " if(depth > 0) ", "//" : " recursiveCreate1 rec1 = new recursiveCreate1(a, depth); ", "//" : " } ", @@ -436,7 +521,7 @@ "//" : " uint depp; ", "//" : " function run(uint depth) ", "//" : " { ", - "//" : " maincontract = msg.sender; ", + "//" : " maincontract = 0x095e7baea6a6c7c4c2dfeb977efac326af552d87; ", "//" : " depp = depth; ", "//" : " recursiveCreate1 rec1 = new recursiveCreate1(maincontract, depth); ", "//" : " } ", @@ -447,13 +532,13 @@ "//" : " address(rec2).send(2); ", "//" : " } ", "//" : "}", - "code" : "0x60003560e060020a90048063820b13f614610021578063a444f5e91461003257005b61002c600435610043565b60006000f35b61003d60043561008f565b60006000f35b600060c66100cc60003960c6600054600160a060020a0316815260200182815260200160006000f0905080600160a060020a0316600060026000600060006000848787f1505050505050565b6000336000819055508160018190555060686101926000396068600054600160a060020a0316815260200182815260200160006000f09050505056006012604060c6600439600451602451601e565b60018060c56000396000f35b6000600182039150600082116031576057565b6068605d600039606883600160a060020a0316815260200182815260200160006000f090505b5050505600601260406068600439600451602451601e565b60018060676000396000f35b60018103905060008111602f576062565b81600160a060020a031663820b13f6600060008260e060020a026000526004858152602001600060008660155a03f15050505b505056000000601260406068600439600451602451601e565b60018060676000396000f35b60018103905060008111602f576062565b81600160a060020a031663820b13f6600060008260e060020a026000526004858152602001600060008660155a03f15050505b5050560000", + "code" : "0x60003560e060020a90048063820b13f614610021578063a444f5e91461003257005b61002c600435610093565b60006000f35b61003d600435610043565b60006000f35b600073095e7baea6a6c7c4c2dfeb977efac326af552d8760008190555081600181905550606b6101ad600039606b600054600160a060020a0316815260200182815260200160006000f090505050565b600060c86100e560003960c8600054600160a060020a0316815260200182815260200160006000f0905080600160a060020a0316600060026000600060006000848787f16100dd57005b50505050505600604060c860043960045160245160006001820391508160008190555060008211602657604c565b606b605d600039606b83600160a060020a0316815260200182815260200160006000f090505b505050600180605c6000396000f300006040606b6004396004516024516001810390508060008190555060008111602457605b565b81600160a060020a031663820b13f6600060008260e060020a026000526004858152602001600060008660325a03f1605857005b50505b5050600180606a6000396000f300006040606b6004396004516024516001810390508060008190555060008111602457605b565b81600160a060020a031663820b13f6600060008260e060020a026000526004858152602001600060008660325a03f1605857005b50505b5050600180606a6000396000f30000", "nonce" : "0", "storage" : { } }, "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { - "balance" : "500000", + "balance" : "5000000", "nonce" : "0", "code" : "", "storage": {} @@ -462,8 +547,8 @@ "transaction" : { "//" : "run(uint256)", - "data" : "0xa444f5e900000000000000000000000000000000000000000000000000000000000204", - "gasLimit" : "30000", + "data" : "0xa444f5e90000000000000000000000000000000000000000000000000000000000000204", + "gasLimit" : "300000", "gasPrice" : "1", "nonce" : "0", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", @@ -508,7 +593,7 @@ } }, "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { - "balance" : "50000", + "balance" : "500000", "nonce" : "0", "code" : "", "storage": {} @@ -518,7 +603,7 @@ { "//" : "run()", "data" : "0xc0406226", - "gasLimit" : "30000", + "gasLimit" : "300000", "gasPrice" : "1", "nonce" : "0", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", diff --git a/test/stTransactionTestFiller.json b/test/stTransactionTestFiller.json index 2f60db437..26a69f187 100644 --- a/test/stTransactionTestFiller.json +++ b/test/stTransactionTestFiller.json @@ -1177,6 +1177,7 @@ }, "transaction" : { + "data" : "cost (0 - 4 !0 - 68) 10*4 + 9*68 = 652", "data" : "0x00000000000000000000112233445566778f32", "gasLimit" : "22000", "gasPrice" : "1", From 7009ab6fe868a506ba94b6342666cd83cd66fd6b Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 11 Mar 2015 16:31:56 +0100 Subject: [PATCH 093/100] Fixed indentation. --- test/Assembly.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/Assembly.cpp b/test/Assembly.cpp index eb76f1184..fbc8e47b5 100644 --- a/test/Assembly.cpp +++ b/test/Assembly.cpp @@ -105,11 +105,11 @@ BOOST_AUTO_TEST_CASE(location_test) shared_ptr n = make_shared("source"); AssemblyItems items = compileContract(sourceCode); vector locations = - vector(11, SourceLocation(2, 75, n)) + - vector(12, SourceLocation(20, 72, n)) + - vector{SourceLocation(42, 51, n), SourceLocation(65, 67, n)} + - vector(4, SourceLocation(58, 67, n)) + - vector(3, SourceLocation(20, 72, n)); + vector(11, SourceLocation(2, 75, n)) + + vector(12, SourceLocation(20, 72, n)) + + vector{SourceLocation(42, 51, n), SourceLocation(65, 67, n)} + + vector(4, SourceLocation(58, 67, n)) + + vector(3, SourceLocation(20, 72, n)); checkAssemblyLocations(items, locations); } From 4a684ea86fe269e027e015caf54589adba086182 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 11 Mar 2015 10:05:41 +0000 Subject: [PATCH 094/100] First draft of recursive bloom. --- libdevcore/Common.cpp | 2 +- libdevcore/FixedHash.h | 8 ---- libethereum/BlockChain.cpp | 94 ++++++++++++++++++++++++++++++++------ libethereum/BlockChain.h | 27 ++++++++++- libethereum/BlockDetails.h | 17 +++++++ libethereum/Client.cpp | 9 ++-- 6 files changed, 129 insertions(+), 28 deletions(-) diff --git a/libdevcore/Common.cpp b/libdevcore/Common.cpp index 431a95580..07e92f542 100644 --- a/libdevcore/Common.cpp +++ b/libdevcore/Common.cpp @@ -27,7 +27,7 @@ using namespace dev; namespace dev { -char const* Version = "0.8.2"; +char const* Version = "0.9.0"; } diff --git a/libdevcore/FixedHash.h b/libdevcore/FixedHash.h index 49c6ed2bf..a52e1cb2e 100644 --- a/libdevcore/FixedHash.h +++ b/libdevcore/FixedHash.h @@ -154,14 +154,6 @@ public: } }; - inline FixedHash<32> bloom() const - { - FixedHash<32> ret; - for (auto i: m_data) - ret[i / 8] |= 1 << (i % 8); - return ret; - } - template inline FixedHash& shiftBloom(FixedHash const& _h) { return (*this |= _h.template nbloom()); diff --git a/libethereum/BlockChain.cpp b/libethereum/BlockChain.cpp index dd0bfad67..54f919647 100644 --- a/libethereum/BlockChain.cpp +++ b/libethereum/BlockChain.cpp @@ -324,6 +324,18 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db) WriteGuard l(x_blockHashes); m_blockHashes[h256(bi.number)].value = newHash; } + { + WriteGuard l(x_blocksBlooms); + LogBloom blockBloom = bi.logBloom; + blockBloom.shiftBloom<3, 32>(sha3(bi.coinbaseAddress.ref())); + unsigned index = (unsigned)bi.number; + for (unsigned level = 0; level < c_bloomIndexLevels; level++, index /= c_bloomIndexSize) + { + unsigned i = index / c_bloomIndexSize % c_bloomIndexSize; + unsigned o = index % c_bloomIndexSize; + m_blocksBlooms[chunkId(level, i)].blooms[o] |= blockBloom; + } + } // Collate transaction hashes and remember who they were. h256s tas; { @@ -346,14 +358,23 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db) m_receipts[newHash] = br; } - m_blocksDB->Put(m_writeOptions, toSlice(newHash), (ldb::Slice)ref(_block)); - m_extrasDB->Put(m_writeOptions, toSlice(newHash, ExtraDetails), (ldb::Slice)dev::ref(m_details[newHash].rlp())); - m_extrasDB->Put(m_writeOptions, toSlice(bi.parentHash, ExtraDetails), (ldb::Slice)dev::ref(m_details[bi.parentHash].rlp())); - m_extrasDB->Put(m_writeOptions, toSlice(h256(bi.number), ExtraBlockHash), (ldb::Slice)dev::ref(m_blockHashes[h256(bi.number)].rlp())); - for (auto const& h: tas) - m_extrasDB->Put(m_writeOptions, toSlice(h, ExtraTransactionAddress), (ldb::Slice)dev::ref(m_transactionAddresses[h].rlp())); - m_extrasDB->Put(m_writeOptions, toSlice(newHash, ExtraLogBlooms), (ldb::Slice)dev::ref(m_logBlooms[newHash].rlp())); - m_extrasDB->Put(m_writeOptions, toSlice(newHash, ExtraReceipts), (ldb::Slice)dev::ref(m_receipts[newHash].rlp())); + { + ReadGuard l1(x_blocksBlooms); + ReadGuard l2(x_details); + ReadGuard l3(x_blockHashes); + ReadGuard l4(x_receipts); + ReadGuard l5(x_logBlooms); + ReadGuard l6(x_transactionAddresses); + m_blocksDB->Put(m_writeOptions, toSlice(newHash), (ldb::Slice)ref(_block)); + m_extrasDB->Put(m_writeOptions, toSlice(newHash, ExtraDetails), (ldb::Slice)dev::ref(m_details[newHash].rlp())); + m_extrasDB->Put(m_writeOptions, toSlice(bi.parentHash, ExtraDetails), (ldb::Slice)dev::ref(m_details[bi.parentHash].rlp())); + m_extrasDB->Put(m_writeOptions, toSlice(h256(bi.number), ExtraBlockHash), (ldb::Slice)dev::ref(m_blockHashes[h256(bi.number)].rlp())); + for (auto const& h: tas) + m_extrasDB->Put(m_writeOptions, toSlice(h, ExtraTransactionAddress), (ldb::Slice)dev::ref(m_transactionAddresses[h].rlp())); + m_extrasDB->Put(m_writeOptions, toSlice(newHash, ExtraLogBlooms), (ldb::Slice)dev::ref(m_logBlooms[newHash].rlp())); + m_extrasDB->Put(m_writeOptions, toSlice(newHash, ExtraReceipts), (ldb::Slice)dev::ref(m_receipts[newHash].rlp())); + m_extrasDB->Put(m_writeOptions, toSlice(newHash, ExtraBlocksBlooms), (ldb::Slice)dev::ref(m_blocksBlooms[newHash].rlp())); + } #if ETH_PARANOIA checkConsistency(); @@ -475,29 +496,30 @@ template static unsigned getHashSize(map const& _map) void BlockChain::updateStats() const { { - ReadGuard l1(x_blocks); + ReadGuard l(x_blocks); m_lastStats.memBlocks = 0; for (auto const& i: m_blocks) m_lastStats.memBlocks += i.second.size() + 64; } { - ReadGuard l2(x_details); + ReadGuard l(x_details); m_lastStats.memDetails = getHashSize(m_details); } { - ReadGuard l5(x_logBlooms); - m_lastStats.memLogBlooms = getHashSize(m_logBlooms); + ReadGuard l1(x_logBlooms); + ReadGuard l2(x_blocksBlooms); + m_lastStats.memLogBlooms = getHashSize(m_logBlooms) + getHashSize(m_blocksBlooms); } { - ReadGuard l4(x_receipts); + ReadGuard l(x_receipts); m_lastStats.memReceipts = getHashSize(m_receipts); } { - ReadGuard l3(x_blockHashes); + ReadGuard l(x_blockHashes); m_lastStats.memBlockHashes = getHashSize(m_blockHashes); } { - ReadGuard l6(x_transactionAddresses); + ReadGuard l(x_transactionAddresses); m_lastStats.memTransactionAddresses = getHashSize(m_transactionAddresses); } } @@ -520,6 +542,7 @@ void BlockChain::garbageCollect(bool _force) WriteGuard l4(x_receipts); WriteGuard l5(x_logBlooms); WriteGuard l6(x_transactionAddresses); + WriteGuard l7(x_blocksBlooms); for (CacheID const& id: m_cacheUsage.back()) { m_inUse.erase(id); @@ -544,6 +567,9 @@ void BlockChain::garbageCollect(bool _force) case ExtraTransactionAddress: m_transactionAddresses.erase(id.first); break; + case ExtraBlocksBlooms: + m_blocksBlooms.erase(id.first); + break; } } m_cacheUsage.pop_back(); @@ -579,6 +605,44 @@ void BlockChain::checkConsistency() delete it; } +static inline unsigned upow(unsigned a, unsigned b) { while (b-- > 0) a *= a; return a; } +static inline unsigned ceilDiv(unsigned n, unsigned d) { return n / (n + d - 1); } +static inline unsigned floorDivPow(unsigned n, unsigned a, unsigned b) { return n / upow(a, b); } +static inline unsigned ceilDivPow(unsigned n, unsigned a, unsigned b) { return ceilDiv(n, upow(a, b)); } + +vector BlockChain::withBlockBloom(LogBloom const& _b, unsigned _earliest, unsigned _latest) const +{ + vector ret; + + // start from the top-level + unsigned u = upow(c_bloomIndexSize, c_bloomIndexLevels - 1); + + // run through each of the top-level blockbloom blocks + for (unsigned index = _earliest / u; index < ceilDiv(_latest, u); ++index) + ret += withBlockBloom(_b, _earliest, _latest, c_bloomIndexLevels - 1, index); + + return ret; +} + +vector BlockChain::withBlockBloom(LogBloom const& _b, unsigned _earliest, unsigned _latest, unsigned _level, unsigned _index) const +{ + vector ret; + +// unsigned u = upow(c_bloomIndexSize, _level); + + BlocksBlooms bb = blocksBlooms(_level, _index); + for (unsigned o = 0; o < 256; ++o) // TODO: crop. + if (bb.blooms[o].contains(_b)) + { + // This level has something like what we want. + if (_level > 0) + ret += withBlockBloom(_b, _earliest, _latest, _level - 1, o + _index * c_bloomIndexSize); + else + ret.push_back(o + _index * c_bloomIndexSize); + } + return ret; +} + h256Set BlockChain::allUnclesFrom(h256 _parent) const { // Get all uncles cited given a parent (i.e. featured as uncles/main in parent, parent + 1, ... parent + 5). diff --git a/libethereum/BlockChain.h b/libethereum/BlockChain.h index 0c1a81066..a6760a3f6 100644 --- a/libethereum/BlockChain.h +++ b/libethereum/BlockChain.h @@ -71,7 +71,8 @@ enum { ExtraBlockHash, ExtraTransactionAddress, ExtraLogBlooms, - ExtraReceipts + ExtraReceipts, + ExtraBlocksBlooms }; /** @@ -132,6 +133,26 @@ public: /// Get a list of transaction hashes for a given block. Thread-safe. h256 numberHash(u256 _index) const { if (!_index) return genesisHash(); return queryExtras(h256(_index), m_blockHashes, x_blockHashes, NullBlockHash).value; } + /** Get the block blooms for a number of blocks. Thread-safe. + * @returns the object pertaining to the blocks: + * level 0: + * 0x, 0x + 1, .. (1x - 1) + * 1x, 1x + 1, .. (2x - 1) + * ... + * (255x .. (256x - 1)) + * level 1: + * 0x .. (1x - 1), 1x .. (2x - 1), ..., (255x .. (256x - 1)) + * 256x .. (257x - 1), 257x .. (258x - 1), ..., (511x .. (512x - 1)) + * ... + * level n, index i, offset o: + * i * (x ^ n) + o * x ^ (n - 1) + */ + BlocksBlooms blocksBlooms(unsigned _level, unsigned _index) const { return blocksBlooms(chunkId(_level, _index)); } + BlocksBlooms blocksBlooms(h256 const& _chunkId) const { return queryExtras(_chunkId, m_blocksBlooms, x_blocksBlooms, NullBlocksBlooms); } + LogBloom blockBloom(unsigned _number) const { return blocksBlooms(chunkId(0, _number / c_bloomIndexSize)).blooms[_number % c_bloomIndexSize]; } + std::vector withBlockBloom(LogBloom const& _b, unsigned _earliest, unsigned _latest) const; + std::vector withBlockBloom(LogBloom const& _b, unsigned _earliest, unsigned _latest, unsigned _topLevel, unsigned _index) const; + /// Get a transaction from its hash. Thread-safe. bytes transaction(h256 _transactionHash) const { TransactionAddress ta = queryExtras(_transactionHash, m_transactionAddresses, x_transactionAddresses, NullTransactionAddress); if (!ta) return bytes(); return transaction(ta.blockHash, ta.index); } @@ -188,6 +209,8 @@ public: void garbageCollect(bool _force = false); private: + static h256 chunkId(unsigned _level, unsigned _index) { return h256(_index * 0xff + _level); } + void open(std::string _path, bool _killExisting = false); void close(); @@ -230,6 +253,8 @@ private: mutable TransactionAddressHash m_transactionAddresses; mutable SharedMutex x_blockHashes; mutable BlockHashHash m_blockHashes; + mutable SharedMutex x_blocksBlooms; + mutable BlocksBloomsHash m_blocksBlooms; using CacheID = std::pair; mutable Mutex x_cacheUsage; diff --git a/libethereum/BlockDetails.h b/libethereum/BlockDetails.h index ed478568d..f3bb73059 100644 --- a/libethereum/BlockDetails.h +++ b/libethereum/BlockDetails.h @@ -36,6 +36,11 @@ namespace dev namespace eth { +// TODO: OPTIMISE: constructors take bytes, RLP used only in necessary classes. + +static const unsigned c_bloomIndexSize = 16; +static const unsigned c_bloomIndexLevels = 6; + struct BlockDetails { BlockDetails(): number(0), totalDifficulty(0) {} @@ -64,6 +69,16 @@ struct BlockLogBlooms mutable unsigned size; }; +struct BlocksBlooms +{ + BlocksBlooms() {} + BlocksBlooms(RLP const& _r) { blooms = _r.toArray(); size = _r.data().size(); } + bytes rlp() const { RLPStream s; s << blooms; size = s.out().size(); return s.out(); } + + std::array blooms; + mutable unsigned size; +}; + struct BlockReceipts { BlockReceipts() {} @@ -103,12 +118,14 @@ using BlockLogBloomsHash = std::map; using BlockReceiptsHash = std::map; using TransactionAddressHash = std::map; using BlockHashHash = std::map; +using BlocksBloomsHash = std::map; static const BlockDetails NullBlockDetails; static const BlockLogBlooms NullBlockLogBlooms; static const BlockReceipts NullBlockReceipts; static const TransactionAddress NullTransactionAddress; static const BlockHash NullBlockHash; +static const BlocksBlooms NullBlocksBlooms; } } diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index b6c310d34..9099986c0 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -871,14 +871,16 @@ LocalisedLogEntries Client::logs(LogFilter const& _f) const int total = 0; #endif // check block bloom - auto info = m_bc.info(h); - auto receipts = m_bc.receipts(h).receipts; - if (_f.matches(info.logBloom)) + auto blockBloom = m_bc.blockBloom(n); + if (_f.matches(blockBloom)) + { + auto receipts = m_bc.receipts(h).receipts; for (size_t i = 0; i < receipts.size(); i++) { TransactionReceipt receipt = receipts[i]; if (_f.matches(receipt.bloom())) { + auto info = m_bc.info(h); auto h = transaction(info.hash, i).sha3(); LogEntries le = _f.matches(receipt); if (le.size()) @@ -901,6 +903,7 @@ LocalisedLogEntries Client::logs(LogFilter const& _f) const #endif } #if ETH_DEBUG + } else skipped++; #endif From c1e71dae80c75f10cfcc472c68200c546c938536 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 11 Mar 2015 16:31:09 +0000 Subject: [PATCH 095/100] Remove skip & max, integrate new heirarchical bloom stuff with everything else. --- alethzero/MainWin.cpp | 12 ++-- libdevcore/FixedHash.h | 6 +- libethereum/BlockChain.cpp | 40 +++++++++++-- libethereum/BlockDetails.h | 2 +- libethereum/Client.cpp | 69 ++++++++--------------- libethereum/LogFilter.cpp | 14 ++++- libethereum/LogFilter.h | 10 +--- libweb3jsonrpc/WebThreeStubServerBase.cpp | 4 -- mix/MixClient.cpp | 12 +--- 9 files changed, 89 insertions(+), 80 deletions(-) diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index ab8e07e7c..165c10f90 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -1012,15 +1012,17 @@ void Main::refreshBlockChain() h256 h(f.toStdString()); if (bc.isKnown(h)) blocks.insert(h); + for (auto const& b: bc.withBlockBloom(LogBloom().shiftBloom<3, 32>(sha3(h)), 0, -1)) + blocks.insert(bc.numberHash(b)); } else if (f.toLongLong() <= bc.number()) blocks.insert(bc.numberHash(u256(f.toLongLong()))); - /*else if (f.size() == 40) + else if (f.size() == 40) { - Address h(f[0]); - if (bc.(h)) - blocks.insert(h); - }*/ + Address h(f.toStdString()); + for (auto const& b: bc.withBlockBloom(LogBloom().shiftBloom<3, 32>(sha3(h)), 0, -1)) + blocks.insert(bc.numberHash(b)); + } QByteArray oldSelected = ui->blocks->count() ? ui->blocks->currentItem()->data(Qt::UserRole).toByteArray() : QByteArray(); ui->blocks->clear(); diff --git a/libdevcore/FixedHash.h b/libdevcore/FixedHash.h index a52e1cb2e..bbc40e66c 100644 --- a/libdevcore/FixedHash.h +++ b/libdevcore/FixedHash.h @@ -156,15 +156,15 @@ public: template inline FixedHash& shiftBloom(FixedHash const& _h) { - return (*this |= _h.template nbloom()); + return (*this |= _h.template bloom()); } template inline bool containsBloom(FixedHash const& _h) { - return contains(_h.template nbloom()); + return contains(_h.template bloom()); } - template inline FixedHash nbloom() const + template inline FixedHash bloom() const { static const unsigned c_bloomBits = M * 8; unsigned mask = c_bloomBits - 1; diff --git a/libethereum/BlockChain.cpp b/libethereum/BlockChain.cpp index 54f919647..95bfcb015 100644 --- a/libethereum/BlockChain.cpp +++ b/libethereum/BlockChain.cpp @@ -610,15 +610,26 @@ static inline unsigned ceilDiv(unsigned n, unsigned d) { return n / (n + d - 1); static inline unsigned floorDivPow(unsigned n, unsigned a, unsigned b) { return n / upow(a, b); } static inline unsigned ceilDivPow(unsigned n, unsigned a, unsigned b) { return ceilDiv(n, upow(a, b)); } +// Level 1 +// [xxx. ] + +// Level 0 +// [.x............F.] +// [........x.......] +// [T.............x.] +// [............ ] + +// F = 14. T = 32 + vector BlockChain::withBlockBloom(LogBloom const& _b, unsigned _earliest, unsigned _latest) const { vector ret; // start from the top-level - unsigned u = upow(c_bloomIndexSize, c_bloomIndexLevels - 1); + unsigned u = upow(c_bloomIndexSize, c_bloomIndexLevels); // run through each of the top-level blockbloom blocks - for (unsigned index = _earliest / u; index < ceilDiv(_latest, u); ++index) + for (unsigned index = _earliest / u; index <= ceilDiv(_latest, u); ++index) // 0 ret += withBlockBloom(_b, _earliest, _latest, c_bloomIndexLevels - 1, index); return ret; @@ -626,12 +637,33 @@ vector BlockChain::withBlockBloom(LogBloom const& _b, unsigned _earlie vector BlockChain::withBlockBloom(LogBloom const& _b, unsigned _earliest, unsigned _latest, unsigned _level, unsigned _index) const { + // 14, 32, 1, 0 + // 14, 32, 0, 0 + // 14, 32, 0, 1 + // 14, 32, 0, 2 + vector ret; -// unsigned u = upow(c_bloomIndexSize, _level); + unsigned uCourse = upow(c_bloomIndexSize, _level + 1); + // 256 + // 16 + unsigned uFine = upow(c_bloomIndexSize, _level); + // 16 + // 1 + + unsigned obegin = _index == _earliest / uCourse ? _earliest / uFine % c_bloomIndexSize : 0; + // 0 + // 14 + // 0 + // 0 + unsigned oend = _index == _latest / uCourse ? (_latest / uFine) % c_bloomIndexSize + 1 : c_bloomIndexSize; + // 3 + // 16 + // 16 + // 1 BlocksBlooms bb = blocksBlooms(_level, _index); - for (unsigned o = 0; o < 256; ++o) // TODO: crop. + for (unsigned o = obegin; o < oend; ++o) if (bb.blooms[o].contains(_b)) { // This level has something like what we want. diff --git a/libethereum/BlockDetails.h b/libethereum/BlockDetails.h index f3bb73059..572ed1888 100644 --- a/libethereum/BlockDetails.h +++ b/libethereum/BlockDetails.h @@ -39,7 +39,7 @@ namespace eth // TODO: OPTIMISE: constructors take bytes, RLP used only in necessary classes. static const unsigned c_bloomIndexSize = 16; -static const unsigned c_bloomIndexLevels = 6; +static const unsigned c_bloomIndexLevels = 2; struct BlockDetails { diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index 9099986c0..165ac20ca 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -833,8 +833,6 @@ LocalisedLogEntries Client::logs(LogFilter const& _f) const LocalisedLogEntries ret; unsigned begin = min(m_bc.number() + 1, (unsigned)_f.latest()); unsigned end = min(m_bc.number(), min(begin, (unsigned)_f.earliest())); - unsigned m = _f.max(); - unsigned s = _f.skip(); // Handle pending transactions differently as they're not on the block chain. if (begin > m_bc.number()) @@ -847,71 +845,52 @@ LocalisedLogEntries Client::logs(LogFilter const& _f) const auto sha3 = m_postMine.pending()[i].sha3(); LogEntries le = _f.matches(tr); if (le.size()) - { - for (unsigned j = 0; j < le.size() && ret.size() != m; ++j) - if (s) - s--; - else - ret.insert(ret.begin(), LocalisedLogEntry(le[j], begin, sha3)); - } + for (unsigned j = 0; j < le.size(); ++j) + ret.insert(ret.begin(), LocalisedLogEntry(le[j], begin, sha3)); } begin = m_bc.number(); } + set matchingBlocks; + for (auto const& i: _f.bloomPossibilities()) + for (auto u: m_bc.withBlockBloom(i, end, begin)) + matchingBlocks.insert(u); + #if ETH_DEBUG - // fill these params - unsigned skipped = 0; unsigned falsePos = 0; #endif - auto h = m_bc.numberHash(begin); - unsigned n = begin; - for (; ret.size() != m && n != end; n--, h = m_bc.details(h).parent) + for (auto n: matchingBlocks) { #if ETH_DEBUG int total = 0; #endif - // check block bloom - auto blockBloom = m_bc.blockBloom(n); - if (_f.matches(blockBloom)) + auto h = m_bc.numberHash(n); + auto receipts = m_bc.receipts(h).receipts; + for (size_t i = 0; i < receipts.size(); i++) { - auto receipts = m_bc.receipts(h).receipts; - for (size_t i = 0; i < receipts.size(); i++) + TransactionReceipt receipt = receipts[i]; + if (_f.matches(receipt.bloom())) { - TransactionReceipt receipt = receipts[i]; - if (_f.matches(receipt.bloom())) + auto info = m_bc.info(h); + auto h = transaction(info.hash, i).sha3(); + LogEntries le = _f.matches(receipt); + if (le.size()) { - auto info = m_bc.info(h); - auto h = transaction(info.hash, i).sha3(); - LogEntries le = _f.matches(receipt); - if (le.size()) - { #if ETH_DEBUG - total += le.size(); + total += le.size(); #endif - for (unsigned j = 0; j < le.size() && ret.size() != m; ++j) - { - if (s) - s--; - else - ret.insert(ret.begin(), LocalisedLogEntry(le[j], n, h)); - } - } + for (unsigned j = 0; j < le.size(); ++j) + ret.insert(ret.begin(), LocalisedLogEntry(le[j], n, h)); } -#if ETH_DEBUG - if (!total) - falsePos++; -#endif } #if ETH_DEBUG - } - else - skipped++; + if (!total) + falsePos++; #endif - if (n == end) - break; + } } #if ETH_DEBUG - cdebug << (begin - n) << "searched; " << skipped << "skipped; " << falsePos << "false +ves"; + cdebug << matchingBlocks.size() << "searched from" << (end - begin) << "skipped; " << falsePos << "false +ves"; #endif return ret; } diff --git a/libethereum/LogFilter.cpp b/libethereum/LogFilter.cpp index 1784094b0..d1b3cf437 100644 --- a/libethereum/LogFilter.cpp +++ b/libethereum/LogFilter.cpp @@ -30,13 +30,13 @@ using namespace dev::eth; std::ostream& dev::eth::operator<<(std::ostream& _out, LogFilter const& _s) { // TODO - _out << "(@" << _s.m_addresses << "#" << _s.m_topics << ">" << _s.m_earliest << "-" << _s.m_latest << "< +" << _s.m_skip << "^" << _s.m_max << ")"; + _out << "(@" << _s.m_addresses << "#" << _s.m_topics << ">" << _s.m_earliest << "-" << _s.m_latest << "< )"; return _out; } void LogFilter::streamRLP(RLPStream& _s) const { - _s.appendList(6) << m_addresses << m_topics << m_earliest << m_latest << m_max << m_skip; + _s.appendList(4) << m_addresses << m_topics << m_earliest << m_latest; } h256 LogFilter::sha3() const @@ -73,6 +73,16 @@ bool LogFilter::matches(State const& _s, unsigned _i) const return matches(_s.receipt(_i)).size() > 0; } +vector LogFilter::bloomPossibilities() const +{ + // return combination of each of the addresses/topics + vector ret; + // TODO proper combinatorics. + for (auto i: m_addresses) + ret.push_back(LogBloom().shiftBloom<3, 32>(dev::sha3(i))); + return ret; +} + LogEntries LogFilter::matches(TransactionReceipt const& _m) const { LogEntries ret; diff --git a/libethereum/LogFilter.h b/libethereum/LogFilter.h index 7b8922a03..a28bfe806 100644 --- a/libethereum/LogFilter.h +++ b/libethereum/LogFilter.h @@ -45,23 +45,21 @@ class State; class LogFilter { public: - LogFilter(int _earliest = 0, int _latest = -1, unsigned _max = 10, unsigned _skip = 0): m_earliest(_earliest), m_latest(_latest), m_max(_max), m_skip(_skip) {} + LogFilter(int _earliest = 0, int _latest = -1): m_earliest(_earliest), m_latest(_latest) {} void streamRLP(RLPStream& _s) const; h256 sha3() const; int earliest() const { return m_earliest; } int latest() const { return m_latest; } - unsigned max() const { return m_max; } - unsigned skip() const { return m_skip; } + + std::vector bloomPossibilities() const; bool matches(LogBloom _bloom) const; bool matches(State const& _s, unsigned _i) const; LogEntries matches(TransactionReceipt const& _r) const; LogFilter address(Address _a) { m_addresses.insert(_a); return *this; } LogFilter topic(unsigned _index, h256 const& _t) { if (_index < 4) m_topics[_index].insert(_t); return *this; } - LogFilter withMax(unsigned _m) { m_max = _m; return *this; } - LogFilter withSkip(unsigned _m) { m_skip = _m; return *this; } LogFilter withEarliest(int _e) { m_earliest = _e; return *this; } LogFilter withLatest(int _e) { m_latest = _e; return *this; } @@ -72,8 +70,6 @@ private: std::array m_topics; int m_earliest = 0; int m_latest = -1; - unsigned m_max = 10; - unsigned m_skip = 0; }; } diff --git a/libweb3jsonrpc/WebThreeStubServerBase.cpp b/libweb3jsonrpc/WebThreeStubServerBase.cpp index 70c8ca9de..09c34feb0 100644 --- a/libweb3jsonrpc/WebThreeStubServerBase.cpp +++ b/libweb3jsonrpc/WebThreeStubServerBase.cpp @@ -128,10 +128,6 @@ static dev::eth::LogFilter toLogFilter(Json::Value const& _json) // commented to filter.withEarliest(_json["earliest"].asInt()); if (_json["latest"].isInt()) filter.withLatest(_json["lastest"].asInt()); - if (_json["max"].isInt()) - filter.withMax(_json["max"].asInt()); - if (_json["skip"].isInt()) - filter.withSkip(_json["skip"].asInt()); if (!_json["address"].empty()) { if (_json["address"].isArray()) diff --git a/mix/MixClient.cpp b/mix/MixClient.cpp index 061057bb4..a06bbb02d 100644 --- a/mix/MixClient.cpp +++ b/mix/MixClient.cpp @@ -331,7 +331,6 @@ eth::LocalisedLogEntries MixClient::logs(eth::LogFilter const& _f) const unsigned lastBlock = bc().number(); unsigned block = std::min(lastBlock, (unsigned)_f.latest()); unsigned end = std::min(lastBlock, std::min(block, (unsigned)_f.earliest())); - unsigned skip = _f.skip(); // Pending transactions if (block > bc().number()) { @@ -341,9 +340,8 @@ eth::LocalisedLogEntries MixClient::logs(eth::LogFilter const& _f) const // Might have a transaction that contains a matching log. TransactionReceipt const& tr = m_state.receipt(i); LogEntries logEntries = _f.matches(tr); - for (unsigned entry = 0; entry < logEntries.size() && ret.size() != _f.max(); ++entry) + for (unsigned entry = 0; entry < logEntries.size(); ++entry) ret.insert(ret.begin(), LocalisedLogEntry(logEntries[entry], block)); - skip -= std::min(skip, static_cast(logEntries.size())); } block = bc().number(); } @@ -355,12 +353,8 @@ eth::LocalisedLogEntries MixClient::logs(eth::LogFilter const& _f) const if (_f.matches(bc().info(h).logBloom)) for (TransactionReceipt receipt: bc().receipts(h).receipts) if (_f.matches(receipt.bloom())) - { - LogEntries logEntries = _f.matches(receipt); - for (unsigned entry = skip; entry < logEntries.size() && ret.size() != _f.max(); ++entry) - ret.insert(ret.begin(), LocalisedLogEntry(logEntries[entry], block)); - skip -= std::min(skip, static_cast(logEntries.size())); - } + for (auto const& e: _f.matches(receipt)) + ret.insert(ret.begin(), LocalisedLogEntry(e, block)); h = bc().details(h).parent; } return ret; From f0e3ddf8f36eddff84259faaa5546974d294d94b Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 11 Mar 2015 16:32:24 +0000 Subject: [PATCH 096/100] Fixes #1275 --- alethzero/MainWin.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index 165c10f90..0fc4d34f6 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -1000,7 +1000,7 @@ void Main::refreshBlockChain() // TODO: keep the same thing highlighted. // TODO: refactor into MVC // TODO: use get by hash/number - // TODO: transactions, log addresses, log topics + // TODO: transactions auto const& bc = ethereum()->blockChain(); QStringList filters = ui->blockChainFilter->text().toLower().split(QRegExp("\\s+"), QString::SkipEmptyParts); From 182fba57b2be332fc856b82de8b76050e7668170 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 11 Mar 2015 18:10:23 +0100 Subject: [PATCH 097/100] Initialize jump type. --- libevmcore/Assembly.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libevmcore/Assembly.h b/libevmcore/Assembly.h index 1cbb12e12..242f6f1ff 100644 --- a/libevmcore/Assembly.h +++ b/libevmcore/Assembly.h @@ -71,7 +71,7 @@ private: AssemblyItemType m_type; u256 m_data; SourceLocation m_location; - JumpType m_jumpType; + JumpType m_jumpType = JumpType::Ordinary; }; using AssemblyItems = std::vector; From 9a4fbc2732687464c881d909f3bbe9771b243b40 Mon Sep 17 00:00:00 2001 From: Matthew Wampler-Doty Date: Wed, 11 Mar 2015 17:27:21 -0700 Subject: [PATCH 098/100] No longer reading caches from disk --- .gitignore | 2 ++ libethcore/Ethasher.cpp | 18 ++++++------------ 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/.gitignore b/.gitignore index 8d8974809..43ed10adf 100644 --- a/.gitignore +++ b/.gitignore @@ -64,6 +64,8 @@ profile DerivedData project.pbxproj +# JetBrains stuff +.idea/ doc/html *.autosave diff --git a/libethcore/Ethasher.cpp b/libethcore/Ethasher.cpp index a28895a1a..a56026459 100644 --- a/libethcore/Ethasher.cpp +++ b/libethcore/Ethasher.cpp @@ -45,18 +45,9 @@ bytes const& Ethasher::cache(BlockInfo const& _header) RecursiveGuard l(x_this); if (!m_caches.count(_header.seedHash)) { - try { - boost::filesystem::create_directories(getDataDir() + "/ethashcache"); - } catch (...) {} - std::string memoFile = getDataDir() + "/ethashcache/" + toHex(_header.seedHash.ref().cropped(0, 4)) + ".cache"; - m_caches[_header.seedHash] = contents(memoFile); - if (m_caches[_header.seedHash].empty()) - { - ethash_params p = params((unsigned)_header.number); - m_caches[_header.seedHash].resize(p.cache_size); - ethash_prep_light(m_caches[_header.seedHash].data(), &p, _header.seedHash.data()); - writeFile(memoFile, m_caches[_header.seedHash]); - } + ethash_params p = params((unsigned)_header.number); + m_caches[_header.seedHash].resize(p.cache_size); + ethash_prep_light(m_caches[_header.seedHash].data(), &p, _header.seedHash.data()); } return m_caches[_header.seedHash]; } @@ -71,6 +62,9 @@ bytesConstRef Ethasher::full(BlockInfo const& _header) delete [] m_fulls.begin()->second.data(); m_fulls.erase(m_fulls.begin()); } + try { + boost::filesystem::create_directories(getDataDir() + "/ethashcache"); + } catch (...) {} std::string memoFile = getDataDir() + "/ethashcache/" + toHex(_header.seedHash.ref().cropped(0, 4)) + ".full"; m_fulls[_header.seedHash] = contentsNew(memoFile); if (!m_fulls[_header.seedHash]) From ee5ac978e50a36c8fa29d355c7767374cbc6de00 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Thu, 12 Mar 2015 10:22:15 +0000 Subject: [PATCH 099/100] Chrono I/O integration. --- chrono_io/chrono_io | 1140 +++++++++++++++++++++++++++++++++++++++++ chrono_io/ratio_io | 601 ++++++++++++++++++++++ libdevcore/CommonIO.h | 1 + 3 files changed, 1742 insertions(+) create mode 100644 chrono_io/chrono_io create mode 100644 chrono_io/ratio_io diff --git a/chrono_io/chrono_io b/chrono_io/chrono_io new file mode 100644 index 000000000..8de9d4f45 --- /dev/null +++ b/chrono_io/chrono_io @@ -0,0 +1,1140 @@ +// chrono_io +// +// (C) Copyright Howard Hinnant +// Use, modification and distribution are subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt). + +#ifndef _CHRONO_IO +#define _CHRONO_IO + +/* + + chrono_io synopsis + +#include +#include + +namespace std +{ +namespace chrono +{ + +template +class durationpunct + : public locale::facet +{ +public: + static locale::id id; + + typedef basic_string string_type; + enum {use_long, use_short}; + + explicit durationpunct(int use = use_long); + + durationpunct(int use, + const string_type& long_seconds, const string_type& long_minutes, + const string_type& long_hours, const string_type& short_seconds, + const string_type& short_minutes, const string_type& short_hours); + + durationpunct(int use, const durationpunct& d); + + template string_type short_name() const; + template string_type long_name() const; + template string_type name() const; + + bool is_short_name() const; + bool is_long_name() const; +}; + +template + basic_ostream& + duration_short(basic_ostream& os); + +template + basic_ostream& + duration_long(basic_ostream& os); + +template + basic_ostream& + operator<<(basic_ostream& os, const duration& d); + +template + basic_istream& + operator>>(basic_istream& is, duration& d); + +template + basic_ostream& + operator<<(basic_ostream& os, + const time_point& tp); + +template + basic_ostream& + operator<<(basic_ostream& os, + const time_point& tp); + +template + basic_ostream& + operator<<(basic_ostream& os, + const time_point& tp); + +template + basic_istream& + operator>>(basic_istream& is, + time_point& tp); + +template + basic_istream& + operator>>(basic_istream& is, + time_point& tp); + +template + basic_istream& + operator>>(basic_istream& is, + time_point& tp); + +template +class timepunct + : public std::locale::facet +{ +public: + typedef std::basic_string string_type; + + static std::locale::id id; + + explicit timepunct(std::size_t refs = 0, + const string_type& fmt = string_type(), + bool local = false); + + explicit timepunct(std::size_t refs, + string_type&& fmt, + bool local = false); + + const string_type& fmt() const {return fmt_;} + bool local() const {return local_;} +}; + +template + unspecfiied + local_fmt(std::basic_string fmt = std::basic_string()); + +template + unspecfiied + local_fmt(const CharT* fmt); + +template + unspecfiied + utc_fmt(std::basic_string fmt = std::basic_string()); + +template + unspecfiied + utc_fmt(const CharT* fmt); + +} // chrono +} // std + +*/ + +#include +#include "ratio_io" + +namespace std { + +namespace chrono +{ + +template +To +round(const duration& d) +{ + To t0 = duration_cast(d); + To t1 = t0; + ++t1; + typedef typename common_type >::type _D; + _D diff0 = d - t0; + _D diff1 = t1 - d; + if (diff0 == diff1) + { + if (t0.count() & 1) + return t1; + return t0; + } + else if (diff0 < diff1) + return t0; + return t1; +} + +template +class durationpunct + : public locale::facet +{ +public: + typedef basic_string<_CharT> string_type; + enum {use_long, use_short}; + +private: + bool __use_short_; + string_type __seconds_; + string_type __minutes_; + string_type __hours_; +public: + static locale::id id; + + explicit durationpunct(size_t refs = 0, + int __use = use_long) + : locale::facet(refs), __use_short_(__use) {} + + durationpunct(size_t refs, int __use, + string_type __seconds, string_type __minutes, + string_type __hours) + : locale::facet(refs), __use_short_(__use), + __seconds_(std::move(__seconds)), + __minutes_(std::move(__minutes)), + __hours_(std::move(__hours)) {} + + bool is_short_name() const {return __use_short_;} + bool is_long_name() const {return !__use_short_;} + + string_type seconds() const {return __seconds_;} + string_type minutes() const {return __minutes_;} + string_type hours() const {return __hours_;} +}; + +template +locale::id +durationpunct<_CharT>::id; + +enum {prefix, symbol}; +enum {utc, local}; + +template +struct __duration_manip +{ + bool __use_short_; + basic_string<_CharT> __seconds_; + basic_string<_CharT> __minutes_; + basic_string<_CharT> __hours_; + + __duration_manip(bool __use_short, basic_string<_CharT> __seconds, + basic_string<_CharT> __minutes, basic_string<_CharT> __hours) + : __use_short_(__use_short), + __seconds_(std::move(__seconds)), + __minutes_(std::move(__minutes)), + __hours_(std::move(__hours)) {} +}; + +class duration_fmt +{ + int form_; +public: + explicit duration_fmt(int f) : form_(f) {} + // explicit + operator int() const {return form_;} +}; + +template +std::basic_ostream& +operator <<(std::basic_ostream& os, duration_fmt d) +{ + os.imbue(std::locale(os.getloc(), new durationpunct(0, d == symbol))); + return os; +} + +template +std::basic_istream& +operator >>(std::basic_istream& is, duration_fmt d) +{ + is.imbue(std::locale(is.getloc(), new durationpunct(0, d == symbol))); + return is; +} + +template +std::basic_ostream& +operator <<(std::basic_ostream& os, __duration_manip m) +{ + os.imbue(std::locale(os.getloc(), new durationpunct(0, m.__use_short_, + std::move(m.__seconds_), std::move(m.__minutes_), std::move(m.__hours_)))); + return os; +} + +template +std::basic_istream& +operator >>(std::basic_istream& is, __duration_manip m) +{ + is.imbue(std::locale(is.getloc(), new durationpunct(0, m.__use_short_, + std::move(m.__seconds_), std::move(m.__minutes_), std::move(m.__hours_)))); + return is; +} + +inline +__duration_manip +duration_short() +{ + return __duration_manip(durationpunct::use_short, "", "", ""); +} + +template +inline +__duration_manip<_CharT> +duration_short(basic_string<_CharT> __seconds, + basic_string<_CharT> __minutes, + basic_string<_CharT> __hours) +{ + return __duration_manip<_CharT>(durationpunct<_CharT>::use_short, + std::move(__seconds), std::move(__minutes), + std::move(__hours)); +} + +inline +__duration_manip +duration_long() +{ + return __duration_manip(durationpunct::use_long, "", "", ""); +} + +template +inline +typename enable_if +< + is_convertible<_T2, basic_string<_CharT> >::value && + is_convertible<_T3, basic_string<_CharT> >::value, + __duration_manip<_CharT> +>::type +duration_long(basic_string<_CharT> __seconds, + _T2 __minutes, + _T3 __hours) +{ + typedef basic_string<_CharT> string_type; + return __duration_manip<_CharT>(durationpunct<_CharT>::use_long, + std::move(__seconds), + string_type(std::move(__minutes)), + string_type(std::move(__hours))); +} + +template +inline +typename enable_if +< + is_convertible<_T2, basic_string<_CharT> >::value && + is_convertible<_T3, basic_string<_CharT> >::value, + __duration_manip<_CharT> +>::type +duration_long(const _CharT* __seconds, + _T2 __minutes, + _T3 __hours) +{ + typedef basic_string<_CharT> string_type; + return __duration_manip<_CharT>(durationpunct<_CharT>::use_long, + string_type(__seconds), + string_type(std::move(__minutes)), + string_type(std::move(__hours))); +} + +template +basic_string<_CharT> +__get_unit(bool __is_long, const basic_string<_CharT>& __seconds, + const basic_string<_CharT>&, const basic_string<_CharT>&, _Period) +{ + if (__is_long) + { + if (__seconds.empty()) + { + _CharT __p[] = {'s', 'e', 'c', 'o', 'n', 'd', 's', 0}; + return ratio_string<_Period, _CharT>::prefix() + __p; + } + return ratio_string<_Period, _CharT>::prefix() + __seconds; + } + if (__seconds.empty()) + return ratio_string<_Period, _CharT>::symbol() + 's'; + return ratio_string<_Period, _CharT>::symbol() + __seconds; +} + +template +inline +basic_string<_CharT> +__get_unit(bool __is_long, const basic_string<_CharT>& __seconds, + const basic_string<_CharT>&, const basic_string<_CharT>&, ratio<1>) +{ + if (__seconds.empty()) + { + if (__is_long) + { + _CharT __p[] = {'s', 'e', 'c', 'o', 'n', 'd', 's'}; + return basic_string<_CharT>(__p, __p + sizeof(__p) / sizeof(_CharT)); + } + else + return basic_string<_CharT>(1, 's'); + } + return __seconds; +} + +template +basic_string<_CharT> +__get_unit(bool __is_long, const basic_string<_CharT>&, + const basic_string<_CharT>& __minutes, + const basic_string<_CharT>&, ratio<60>) +{ + if (__minutes.empty()) + { + if (__is_long) + { + _CharT __p[] = {'m', 'i', 'n', 'u', 't', 'e', 's'}; + return basic_string<_CharT>(__p, __p + sizeof(__p) / sizeof(_CharT)); + } + else + return basic_string<_CharT>(1, 'm'); + } + return __minutes; +} + +template +inline +basic_string<_CharT> +__get_unit(bool __is_long, const basic_string<_CharT>&, + const basic_string<_CharT>&, + const basic_string<_CharT>& __hours, ratio<3600>) +{ + if (__hours.empty()) + { + if (__is_long) + { + _CharT __p[] = {'h', 'o', 'u', 'r', 's'}; + return basic_string<_CharT>(__p, __p + sizeof(__p) / sizeof(_CharT)); + } + else + return basic_string<_CharT>(1, 'h'); + } + return __hours; +} + +template +basic_ostream<_CharT, _Traits>& +operator<<(basic_ostream<_CharT, _Traits>& __os, const duration<_Rep, _Period>& __d) +{ + typename basic_ostream<_CharT, _Traits>::sentry ok(__os); + if (ok) + { + typedef durationpunct<_CharT> _F; + typedef basic_string<_CharT> string_type; + bool failed = false; + try + { + bool __is_long = true; + string_type __seconds; + string_type __minutes; + string_type __hours; + locale __loc = __os.getloc(); + if (has_facet<_F>(__loc)) + { + const _F& f = use_facet<_F>(__loc); + __is_long = f.is_long_name(); + __seconds = f.seconds(); + __minutes = f.minutes(); + __hours = f.hours(); + } + string_type __unit = __get_unit(__is_long, __seconds, __minutes, + __hours, typename _Period::type()); + __os << __d.count() << ' ' << __unit; + } + catch (...) + { + failed = true; + } + if (failed) + __os.setstate(ios_base::failbit | ios_base::badbit); + } + return __os; +} + +template ::value> +struct __duration_io_intermediate +{ + typedef _Rep type; +}; + +template +struct __duration_io_intermediate<_Rep, true> +{ + typedef typename conditional + < + is_floating_point<_Rep>::value, + long double, + typename conditional + < + is_signed<_Rep>::value, + long long, + unsigned long long + >::type + >::type type; +}; + +template +T +__gcd(T x, T y) +{ + while (y != 0) + { + T old_x = x; + x = y; + y = old_x % y; + } + return x; +} + +template <> +long double +inline +__gcd(long double x, long double y) +{ + (void)x; + (void)y; + return 1; +} + +template +basic_istream<_CharT, _Traits>& +operator>>(basic_istream<_CharT, _Traits>& __is, duration<_Rep, _Period>& __d) +{ + typedef basic_string<_CharT> string_type; + typedef durationpunct<_CharT> _F; + typedef typename __duration_io_intermediate<_Rep>::type _IR; + _IR __r; + // read value into __r + __is >> __r; + if (__is.good()) + { + // now determine unit + typedef istreambuf_iterator<_CharT, _Traits> _I; + _I __i(__is); + _I __e; + if (__i != __e && *__i == ' ') // mandatory ' ' after value + { + ++__i; + if (__i != __e) + { + string_type __seconds; + string_type __minutes; + string_type __hours; + locale __loc = __is.getloc(); + if (has_facet<_F>(__loc)) + { + const _F& f = use_facet<_F>(__loc); + __seconds = f.seconds(); + __minutes = f.minutes(); + __hours = f.hours(); + } + // unit is num / den (yet to be determined) + unsigned long long num = 0; + unsigned long long den = 0; + if (*__i == '[') + { + // parse [N/D]s or [N/D]seconds format + ++__i; + _CharT __x; + __is >> num >> __x >> den; + if (!__is.good() || __x != '/') + { + __is.setstate(__is.failbit); + return __is; + } + __i = _I(__is); + if (*__i != ']') + { + __is.setstate(__is.failbit); + return __is; + } + ++__i; + const basic_string<_CharT> __units[] = + { + __get_unit(true, __seconds, __minutes, __hours, ratio<1>()), + __get_unit(false, __seconds, __minutes, __hours, ratio<1>()) + }; + ios_base::iostate __err = ios_base::goodbit; + const basic_string<_CharT>* __k = __scan_keyword(__i, __e, + __units, __units + sizeof(__units)/sizeof(__units[0]), + use_facet >(__loc), + __err); + switch ((__k - __units) / 2) + { + case 0: + break; + default: + __is.setstate(__err); + return __is; + } + } + else + { + // parse SI name, short or long + const basic_string<_CharT> __units[] = + { + __get_unit(true, __seconds, __minutes, __hours, atto()), + __get_unit(false, __seconds, __minutes, __hours, atto()), + __get_unit(true, __seconds, __minutes, __hours, femto()), + __get_unit(false, __seconds, __minutes, __hours, femto()), + __get_unit(true, __seconds, __minutes, __hours, pico()), + __get_unit(false, __seconds, __minutes, __hours, pico()), + __get_unit(true, __seconds, __minutes, __hours, nano()), + __get_unit(false, __seconds, __minutes, __hours, nano()), + __get_unit(true, __seconds, __minutes, __hours, micro()), + __get_unit(false, __seconds, __minutes, __hours, micro()), + __get_unit(true, __seconds, __minutes, __hours, milli()), + __get_unit(false, __seconds, __minutes, __hours, milli()), + __get_unit(true, __seconds, __minutes, __hours, centi()), + __get_unit(false, __seconds, __minutes, __hours, centi()), + __get_unit(true, __seconds, __minutes, __hours, deci()), + __get_unit(false, __seconds, __minutes, __hours, deci()), + __get_unit(true, __seconds, __minutes, __hours, deca()), + __get_unit(false, __seconds, __minutes, __hours, deca()), + __get_unit(true, __seconds, __minutes, __hours, hecto()), + __get_unit(false, __seconds, __minutes, __hours, hecto()), + __get_unit(true, __seconds, __minutes, __hours, kilo()), + __get_unit(false, __seconds, __minutes, __hours, kilo()), + __get_unit(true, __seconds, __minutes, __hours, mega()), + __get_unit(false, __seconds, __minutes, __hours, mega()), + __get_unit(true, __seconds, __minutes, __hours, giga()), + __get_unit(false, __seconds, __minutes, __hours, giga()), + __get_unit(true, __seconds, __minutes, __hours, tera()), + __get_unit(false, __seconds, __minutes, __hours, tera()), + __get_unit(true, __seconds, __minutes, __hours, peta()), + __get_unit(false, __seconds, __minutes, __hours, peta()), + __get_unit(true, __seconds, __minutes, __hours, exa()), + __get_unit(false, __seconds, __minutes, __hours, exa()), + __get_unit(true, __seconds, __minutes, __hours, ratio<1>()), + __get_unit(false, __seconds, __minutes, __hours, ratio<1>()), + __get_unit(true, __seconds, __minutes, __hours, ratio<60>()), + __get_unit(false, __seconds, __minutes, __hours, ratio<60>()), + __get_unit(true, __seconds, __minutes, __hours, ratio<3600>()), + __get_unit(false, __seconds, __minutes, __hours, ratio<3600>()) + }; + ios_base::iostate __err = ios_base::goodbit; + const basic_string<_CharT>* __k = __scan_keyword(__i, __e, + __units, __units + sizeof(__units)/sizeof(__units[0]), + use_facet >(__loc), + __err); + switch ((__k - __units) / 2) + { + case 0: + num = 1ULL; + den = 1000000000000000000ULL; + break; + case 1: + num = 1ULL; + den = 1000000000000000ULL; + break; + case 2: + num = 1ULL; + den = 1000000000000ULL; + break; + case 3: + num = 1ULL; + den = 1000000000ULL; + break; + case 4: + num = 1ULL; + den = 1000000ULL; + break; + case 5: + num = 1ULL; + den = 1000ULL; + break; + case 6: + num = 1ULL; + den = 100ULL; + break; + case 7: + num = 1ULL; + den = 10ULL; + break; + case 8: + num = 10ULL; + den = 1ULL; + break; + case 9: + num = 100ULL; + den = 1ULL; + break; + case 10: + num = 1000ULL; + den = 1ULL; + break; + case 11: + num = 1000000ULL; + den = 1ULL; + break; + case 12: + num = 1000000000ULL; + den = 1ULL; + break; + case 13: + num = 1000000000000ULL; + den = 1ULL; + break; + case 14: + num = 1000000000000000ULL; + den = 1ULL; + break; + case 15: + num = 1000000000000000000ULL; + den = 1ULL; + break; + case 16: + num = 1; + den = 1; + break; + case 17: + num = 60; + den = 1; + break; + case 18: + num = 3600; + den = 1; + break; + default: + __is.setstate(__err); + return __is; + } + } + // unit is num/den + // __r should be multiplied by (num/den) / _Period + // Reduce (num/den) / _Period to lowest terms + unsigned long long __gcd_n1_n2 = __gcd(num, _Period::num); + unsigned long long __gcd_d1_d2 = __gcd(den, _Period::den); + num /= __gcd_n1_n2; + den /= __gcd_d1_d2; + unsigned long long __n2 = _Period::num / __gcd_n1_n2; + unsigned long long __d2 = _Period::den / __gcd_d1_d2; + if (num > numeric_limits::max() / __d2 || + den > numeric_limits::max() / __n2) + { + // (num/den) / _Period overflows + __is.setstate(__is.failbit); + return __is; + } + num *= __d2; + den *= __n2; + // num / den is now factor to multiply by __r + typedef typename common_type<_IR, unsigned long long>::type _CT; + if (is_integral<_IR>::value) + { + // Reduce __r * num / den + _CT __t = __gcd<_CT>(__r, den); + __r /= __t; + den /= __t; + if (den != 1) + { + // Conversion to _Period is integral and not exact + __is.setstate(__is.failbit); + return __is; + } + } + if (__r > duration_values<_CT>::max() / num) + { + // Conversion to _Period overflowed + __is.setstate(__is.failbit); + return __is; + } + _CT __t = __r * num; + __t /= den; + if (duration_values<_Rep>::max() < __t) + { + // Conversion to _Period overflowed + __is.setstate(__is.failbit); + return __is; + } + // Success! Store it. + __r = _Rep(__t); + __d = duration<_Rep, _Period>(__r); + } + else + __is.setstate(__is.failbit | __is.eofbit); + } + else + { + if (__i == __e) + __is.setstate(__is.eofbit); + __is.setstate(__is.failbit); + } + } + else + __is.setstate(__is.failbit); + return __is; +} + +template +class timepunct + : public std::locale::facet +{ +public: + typedef std::basic_string string_type; + +private: + string_type fmt_; + bool local_; + +public: + static std::locale::id id; + + explicit timepunct(std::size_t refs = 0, + const string_type& fmt = string_type(), + bool local = false) + : std::locale::facet(refs), + fmt_(fmt), + local_(local) {} + + explicit timepunct(std::size_t refs, + string_type&& fmt, + bool local = false) + : std::locale::facet(refs), + fmt_(std::move(fmt)), + local_(local) {} + + const string_type& fmt() const {return fmt_;} + bool local() const {return local_;} +}; + +template +std::locale::id +timepunct::id; + +template +basic_ostream<_CharT, _Traits>& +operator<<(basic_ostream<_CharT, _Traits>& __os, + const time_point& __tp) +{ + return __os << __tp.time_since_epoch() << " since boot"; +} + +template +struct __time_manip +{ + std::basic_string fmt_; + bool local_; + + __time_manip(std::basic_string fmt, bool local) + : fmt_(std::move(fmt)), + local_(local) {} +}; + +template +std::basic_ostream& +operator <<(std::basic_ostream& os, __time_manip m) +{ + os.imbue(std::locale(os.getloc(), new timepunct(0, std::move(m.fmt_), m.local_))); + return os; +} + +template +std::basic_istream& +operator >>(std::basic_istream& is, __time_manip m) +{ + is.imbue(std::locale(is.getloc(), new timepunct(0, std::move(m.fmt_), m.local_))); + return is; +} + +template +inline +__time_manip +local_fmt(std::basic_string fmt) +{ + return __time_manip(std::move(fmt), true); +} + +template +inline +__time_manip +local_fmt(const charT* fmt) +{ + return __time_manip(fmt, true); +} + +inline +__time_manip +local_fmt() +{ + return __time_manip("", true); +} + +template +inline +__time_manip +utc_fmt(std::basic_string fmt) +{ + return __time_manip(std::move(fmt), false); +} + +template +inline +__time_manip +utc_fmt(const charT* fmt) +{ + return __time_manip(fmt, false); +} + +inline +__time_manip +utc_fmt() +{ + return __time_manip("", false); +} + +class __time_man +{ + int form_; +public: + explicit __time_man(int f) : form_(f) {} + // explicit + operator int() const {return form_;} +}; + +template +std::basic_ostream& +operator <<(std::basic_ostream& os, __time_man m) +{ + os.imbue(std::locale(os.getloc(), new timepunct(0, basic_string(), m == local))); + return os; +} + +template +std::basic_istream& +operator >>(std::basic_istream& is, __time_man m) +{ + is.imbue(std::locale(is.getloc(), new timepunct(0, basic_string(), m == local))); + return is; +} + + +inline +__time_man +time_fmt(int f) +{ + return __time_man(f); +} + +template +basic_istream<_CharT, _Traits>& +operator>>(basic_istream<_CharT, _Traits>& __is, + time_point& __tp) +{ + _Duration __d; + __is >> __d; + if (__is.good()) + { + const _CharT __u[] = {' ', 's', 'i', 'n', 'c', 'e', ' ', 'b', 'o', 'o', 't'}; + const basic_string<_CharT> __units(__u, __u + sizeof(__u)/sizeof(__u[0])); + ios_base::iostate __err = ios_base::goodbit; + typedef istreambuf_iterator<_CharT, _Traits> _I; + _I __i(__is); + _I __e; + ptrdiff_t __k = __scan_keyword(__i, __e, + &__units, &__units + 1, + use_facet >(__is.getloc()), + __err) - &__units; + if (__k == 1) + { + // failed to read epoch string + __is.setstate(__err); + return __is; + } + __tp = time_point(__d); + } + else + __is.setstate(__is.failbit); + return __is; +} + +template +basic_ostream<_CharT, _Traits>& +operator<<(basic_ostream<_CharT, _Traits>& __os, + const time_point& __tp) +{ + typename basic_ostream<_CharT, _Traits>::sentry ok(__os); + if (ok) + { + bool failed = false; + try + { + const _CharT* pb = nullptr; + const _CharT* pe = pb; + bool __local = false; + typedef timepunct<_CharT> F; + locale loc = __os.getloc(); + if (has_facet(loc)) + { + const F& f = use_facet(loc); + pb = f.fmt().data(); + pe = pb + f.fmt().size(); + __local = f.local(); + } + time_t __t = system_clock::to_time_t(__tp); + tm __tm; + if (__local) + { + if (localtime_r(&__t, &__tm) == 0) + failed = true; + } + else + { + if (gmtime_r(&__t, &__tm) == 0) + failed = true; + } + if (!failed) + { + const time_put<_CharT>& tp = use_facet >(loc); + if (pb == pe) + { + _CharT pattern[] = {'%', 'F', ' ', '%', 'H', ':', '%', 'M', ':'}; + pb = pattern; + pe = pb + sizeof(pattern) / sizeof(_CharT); + failed = tp.put(__os, __os, __os.fill(), &__tm, pb, pe).failed(); + if (!failed) + { + duration __d = __tp - system_clock::from_time_t(__t) + + seconds(__tm.tm_sec); + if (__d.count() < 10) + __os << _CharT('0'); + ios::fmtflags __flgs = __os.flags(); + __os.setf(ios::fixed, ios::floatfield); + __os << __d.count(); + __os.flags(__flgs); + if (__local) + { + _CharT sub_pattern[] = {' ', '%', 'z'}; + pb = sub_pattern; + pe = pb + + sizeof(sub_pattern) / sizeof(_CharT); + failed = tp.put(__os, __os, __os.fill(), &__tm, pb, pe).failed(); + } + else + { + _CharT sub_pattern[] = {' ', '+', '0', '0', '0', '0', 0}; + __os << sub_pattern; + } + } + } + else + failed = tp.put(__os, __os, __os.fill(), &__tm, pb, pe).failed(); + } + } + catch (...) + { + failed = true; + } + if (failed) + __os.setstate(ios_base::failbit | ios_base::badbit); + } + return __os; +} + +template +basic_istream<_CharT, _Traits>& +operator>>(basic_istream<_CharT, _Traits>& __is, + time_point& __tp) +{ + typename basic_istream<_CharT,_Traits>::sentry ok(__is); + if (ok) + { + ios_base::iostate err = ios_base::goodbit; + try + { + const _CharT* pb = nullptr; + const _CharT* pe = pb; + bool __local = false; + typedef timepunct<_CharT> F; + locale loc = __is.getloc(); + if (has_facet(loc)) + { + const F& f = use_facet(loc); + pb = f.fmt().data(); + pe = pb + f.fmt().size(); + __local = f.local(); + } + const time_get<_CharT>& tg = use_facet >(loc); + tm __tm = {0}; + if (pb == pe) + { + _CharT pattern[] = {'%', 'Y', '-', '%', 'm', '-', '%', 'd', + ' ', '%', 'H', ':', '%', 'M', ':'}; + pb = pattern; + pe = pb + sizeof(pattern) / sizeof(_CharT); + tg.get(__is, 0, __is, err, &__tm, pb, pe); + if (err & ios_base::failbit) + goto __exit; + typedef istreambuf_iterator<_CharT, _Traits> _I; + double __sec; + _CharT __c = _CharT(); + __is >> __sec; + if (__is.fail()) + { + err |= ios_base::failbit; + goto __exit; + } + _I __i(__is); + _I __eof; + __c = *__i; + if (++__i == __eof || __c != ' ') + { + err |= ios_base::failbit; + goto __exit; + } + __c = *__i; + if (++__i == __eof || (__c != '+' && __c != '-')) + { + err |= ios_base::failbit; + goto __exit; + } + int __n = __c == '-' ? 1 : -1; + __c = *__i; + if (++__i == __eof || !isdigit(__c)) + { + err |= ios_base::failbit; + goto __exit; + } + int __min = (__c - '0') * 600; + __c = *__i; + if (++__i == __eof || !isdigit(__c)) + { + err |= ios_base::failbit; + goto __exit; + } + __min += (__c - '0') * 60; + __c = *__i; + if (++__i == __eof || !isdigit(__c)) + { + err |= ios_base::failbit; + goto __exit; + } + __min += (__c - '0') * 10; + __c = *__i; + if (!isdigit(__c)) + { + err |= ios_base::failbit; + goto __exit; + } + ++__i; + __min += __c - '0'; + __min *= __n; + time_t __t; + __t = timegm(&__tm); + __tp = system_clock::from_time_t(__t) + minutes(__min) + + round(duration(__sec)); + } + else + { + tg.get(__is, 0, __is, err, &__tm, pb, pe); + } + } + catch (...) + { + err |= ios_base::badbit | ios_base::failbit; + } + __exit: + __is.setstate(err); + } + return __is; +} + +} // chrono + +} + +#endif // _CHRONO_IO diff --git a/chrono_io/ratio_io b/chrono_io/ratio_io new file mode 100644 index 000000000..ade52376c --- /dev/null +++ b/chrono_io/ratio_io @@ -0,0 +1,601 @@ +// ratio_io +// +// (C) Copyright Howard Hinnant +// Use, modification and distribution are subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt). + +#ifndef _RATIO_IO +#define _RATIO_IO + +/* + + ratio_io synopsis + +#include +#include + +namespace std +{ + +template +struct ratio_string +{ + static basic_string symbol(); + static basic_string prefix(); +}; + +} // std + +*/ + +#include +#include +#include + +namespace std { + +template +struct ratio_string +{ + static basic_string<_CharT> symbol() {return prefix();} + static basic_string<_CharT> prefix(); +}; + +template +basic_string<_CharT> +ratio_string<_Ratio, _CharT>::prefix() +{ + basic_ostringstream<_CharT> __os; + __os << _CharT('[') << _Ratio::num << _CharT('/') + << _Ratio::den << _CharT(']'); + return __os.str(); +} + +// atto + +template <> +struct ratio_string +{ + static string symbol() {return string(1, 'a');} + static string prefix() {return string("atto");} +}; + +#if HAS_UNICODE_SUPPORT + +template <> +struct ratio_string +{ + static u16string symbol() {return u16string(1, u'a');} + static u16string prefix() {return u16string(u"atto");} +}; + +template <> +struct ratio_string +{ + static u32string symbol() {return u32string(1, U'a');} + static u32string prefix() {return u32string(U"atto");} +}; + +#endif + +template <> +struct ratio_string +{ + static wstring symbol() {return wstring(1, L'a');} + static wstring prefix() {return wstring(L"atto");} +}; + +// femto + +template <> +struct ratio_string +{ + static string symbol() {return string(1, 'f');} + static string prefix() {return string("femto");} +}; + +#if HAS_UNICODE_SUPPORT + +template <> +struct ratio_string +{ + static u16string symbol() {return u16string(1, u'f');} + static u16string prefix() {return u16string(u"femto");} +}; + +template <> +struct ratio_string +{ + static u32string symbol() {return u32string(1, U'f');} + static u32string prefix() {return u32string(U"femto");} +}; + +#endif + +template <> +struct ratio_string +{ + static wstring symbol() {return wstring(1, L'f');} + static wstring prefix() {return wstring(L"femto");} +}; + +// pico + +template <> +struct ratio_string +{ + static string symbol() {return string(1, 'p');} + static string prefix() {return string("pico");} +}; + +#if HAS_UNICODE_SUPPORT + +template <> +struct ratio_string +{ + static u16string symbol() {return u16string(1, u'p');} + static u16string prefix() {return u16string(u"pico");} +}; + +template <> +struct ratio_string +{ + static u32string symbol() {return u32string(1, U'p');} + static u32string prefix() {return u32string(U"pico");} +}; + +#endif + +template <> +struct ratio_string +{ + static wstring symbol() {return wstring(1, L'p');} + static wstring prefix() {return wstring(L"pico");} +}; + +// nano + +template <> +struct ratio_string +{ + static string symbol() {return string(1, 'n');} + static string prefix() {return string("nano");} +}; + +#if HAS_UNICODE_SUPPORT + +template <> +struct ratio_string +{ + static u16string symbol() {return u16string(1, u'n');} + static u16string prefix() {return u16string(u"nano");} +}; + +template <> +struct ratio_string +{ + static u32string symbol() {return u32string(1, U'n');} + static u32string prefix() {return u32string(U"nano");} +}; + +#endif + +template <> +struct ratio_string +{ + static wstring symbol() {return wstring(1, L'n');} + static wstring prefix() {return wstring(L"nano");} +}; + +// micro + +template <> +struct ratio_string +{ + static string symbol() {return string("\xC2\xB5");} + static string prefix() {return string("micro");} +}; + +#if HAS_UNICODE_SUPPORT + +template <> +struct ratio_string +{ + static u16string symbol() {return u16string(1, u'\xB5');} + static u16string prefix() {return u16string(u"micro");} +}; + +template <> +struct ratio_string +{ + static u32string symbol() {return u32string(1, U'\xB5');} + static u32string prefix() {return u32string(U"micro");} +}; + +#endif + +template <> +struct ratio_string +{ + static wstring symbol() {return wstring(1, L'\xB5');} + static wstring prefix() {return wstring(L"micro");} +}; + +// milli + +template <> +struct ratio_string +{ + static string symbol() {return string(1, 'm');} + static string prefix() {return string("milli");} +}; + +#if HAS_UNICODE_SUPPORT + +template <> +struct ratio_string +{ + static u16string symbol() {return u16string(1, u'm');} + static u16string prefix() {return u16string(u"milli");} +}; + +template <> +struct ratio_string +{ + static u32string symbol() {return u32string(1, U'm');} + static u32string prefix() {return u32string(U"milli");} +}; + +#endif + +template <> +struct ratio_string +{ + static wstring symbol() {return wstring(1, L'm');} + static wstring prefix() {return wstring(L"milli");} +}; + +// centi + +template <> +struct ratio_string +{ + static string symbol() {return string(1, 'c');} + static string prefix() {return string("centi");} +}; + +#if HAS_UNICODE_SUPPORT + +template <> +struct ratio_string +{ + static u16string symbol() {return u16string(1, u'c');} + static u16string prefix() {return u16string(u"centi");} +}; + +template <> +struct ratio_string +{ + static u32string symbol() {return u32string(1, U'c');} + static u32string prefix() {return u32string(U"centi");} +}; + +#endif + +template <> +struct ratio_string +{ + static wstring symbol() {return wstring(1, L'c');} + static wstring prefix() {return wstring(L"centi");} +}; + +// deci + +template <> +struct ratio_string +{ + static string symbol() {return string(1, 'd');} + static string prefix() {return string("deci");} +}; + +#if HAS_UNICODE_SUPPORT + +template <> +struct ratio_string +{ + static u16string symbol() {return u16string(1, u'd');} + static u16string prefix() {return u16string(u"deci");} +}; + +template <> +struct ratio_string +{ + static u32string symbol() {return u32string(1, U'd');} + static u32string prefix() {return u32string(U"deci");} +}; + +#endif + +template <> +struct ratio_string +{ + static wstring symbol() {return wstring(1, L'd');} + static wstring prefix() {return wstring(L"deci");} +}; + +// deca + +template <> +struct ratio_string +{ + static string symbol() {return string("da");} + static string prefix() {return string("deca");} +}; + +#if HAS_UNICODE_SUPPORT + +template <> +struct ratio_string +{ + static u16string symbol() {return u16string(u"da");} + static u16string prefix() {return u16string(u"deca");} +}; + +template <> +struct ratio_string +{ + static u32string symbol() {return u32string(U"da");} + static u32string prefix() {return u32string(U"deca");} +}; + +#endif + +template <> +struct ratio_string +{ + static wstring symbol() {return wstring(L"da");} + static wstring prefix() {return wstring(L"deca");} +}; + +// hecto + +template <> +struct ratio_string +{ + static string symbol() {return string(1, 'h');} + static string prefix() {return string("hecto");} +}; + +#if HAS_UNICODE_SUPPORT + +template <> +struct ratio_string +{ + static u16string symbol() {return u16string(1, u'h');} + static u16string prefix() {return u16string(u"hecto");} +}; + +template <> +struct ratio_string +{ + static u32string symbol() {return u32string(1, U'h');} + static u32string prefix() {return u32string(U"hecto");} +}; + +#endif + +template <> +struct ratio_string +{ + static wstring symbol() {return wstring(1, L'h');} + static wstring prefix() {return wstring(L"hecto");} +}; + +// kilo + +template <> +struct ratio_string +{ + static string symbol() {return string(1, 'k');} + static string prefix() {return string("kilo");} +}; + +#if HAS_UNICODE_SUPPORT + +template <> +struct ratio_string +{ + static u16string symbol() {return u16string(1, u'k');} + static u16string prefix() {return u16string(u"kilo");} +}; + +template <> +struct ratio_string +{ + static u32string symbol() {return u32string(1, U'k');} + static u32string prefix() {return u32string(U"kilo");} +}; + +#endif + +template <> +struct ratio_string +{ + static wstring symbol() {return wstring(1, L'k');} + static wstring prefix() {return wstring(L"kilo");} +}; + +// mega + +template <> +struct ratio_string +{ + static string symbol() {return string(1, 'M');} + static string prefix() {return string("mega");} +}; + +#if HAS_UNICODE_SUPPORT + +template <> +struct ratio_string +{ + static u16string symbol() {return u16string(1, u'M');} + static u16string prefix() {return u16string(u"mega");} +}; + +template <> +struct ratio_string +{ + static u32string symbol() {return u32string(1, U'M');} + static u32string prefix() {return u32string(U"mega");} +}; + +#endif + +template <> +struct ratio_string +{ + static wstring symbol() {return wstring(1, L'M');} + static wstring prefix() {return wstring(L"mega");} +}; + +// giga + +template <> +struct ratio_string +{ + static string symbol() {return string(1, 'G');} + static string prefix() {return string("giga");} +}; + +#if HAS_UNICODE_SUPPORT + +template <> +struct ratio_string +{ + static u16string symbol() {return u16string(1, u'G');} + static u16string prefix() {return u16string(u"giga");} +}; + +template <> +struct ratio_string +{ + static u32string symbol() {return u32string(1, U'G');} + static u32string prefix() {return u32string(U"giga");} +}; + +#endif + +template <> +struct ratio_string +{ + static wstring symbol() {return wstring(1, L'G');} + static wstring prefix() {return wstring(L"giga");} +}; + +// tera + +template <> +struct ratio_string +{ + static string symbol() {return string(1, 'T');} + static string prefix() {return string("tera");} +}; + +#if HAS_UNICODE_SUPPORT + +template <> +struct ratio_string +{ + static u16string symbol() {return u16string(1, u'T');} + static u16string prefix() {return u16string(u"tera");} +}; + +template <> +struct ratio_string +{ + static u32string symbol() {return u32string(1, U'T');} + static u32string prefix() {return u32string(U"tera");} +}; + +#endif + +template <> +struct ratio_string +{ + static wstring symbol() {return wstring(1, L'T');} + static wstring prefix() {return wstring(L"tera");} +}; + +// peta + +template <> +struct ratio_string +{ + static string symbol() {return string(1, 'P');} + static string prefix() {return string("peta");} +}; + +#if HAS_UNICODE_SUPPORT + +template <> +struct ratio_string +{ + static u16string symbol() {return u16string(1, u'P');} + static u16string prefix() {return u16string(u"peta");} +}; + +template <> +struct ratio_string +{ + static u32string symbol() {return u32string(1, U'P');} + static u32string prefix() {return u32string(U"peta");} +}; + +#endif + +template <> +struct ratio_string +{ + static wstring symbol() {return wstring(1, L'P');} + static wstring prefix() {return wstring(L"peta");} +}; + +// exa + +template <> +struct ratio_string +{ + static string symbol() {return string(1, 'E');} + static string prefix() {return string("exa");} +}; + +#if HAS_UNICODE_SUPPORT + +template <> +struct ratio_string +{ + static u16string symbol() {return u16string(1, u'E');} + static u16string prefix() {return u16string(u"exa");} +}; + +template <> +struct ratio_string +{ + static u32string symbol() {return u32string(1, U'E');} + static u32string prefix() {return u32string(U"exa");} +}; + +#endif + +template <> +struct ratio_string +{ + static wstring symbol() {return wstring(1, L'E');} + static wstring prefix() {return wstring(L"exa");} +}; + +} + +#endif // _RATIO_IO diff --git a/libdevcore/CommonIO.h b/libdevcore/CommonIO.h index 03fc9cffc..314600e43 100644 --- a/libdevcore/CommonIO.h +++ b/libdevcore/CommonIO.h @@ -35,6 +35,7 @@ #include #include #include +#include #include "Common.h" #include "Base64.h" From ba5598892d9256cbdc53858f17a52d0b3c83bcf8 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Thu, 12 Mar 2015 11:12:03 +0000 Subject: [PATCH 100/100] Bump database version. --- libethcore/Common.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libethcore/Common.cpp b/libethcore/Common.cpp index 9eb622fe3..8bd4ef143 100644 --- a/libethcore/Common.cpp +++ b/libethcore/Common.cpp @@ -33,7 +33,7 @@ namespace eth { const unsigned c_protocolVersion = 56; -const unsigned c_databaseBaseVersion = 7; +const unsigned c_databaseBaseVersion = 8; #if ETH_FATDB const unsigned c_databaseVersionModifier = 1000; #else