Add manifests subscriptions

Add verify method to manifest script to check signature
This commit is contained in:
wilsonianb
2016-01-13 15:44:47 -08:00
committed by Edward Hennis
parent a67e4ab9f1
commit 749b4adc7c
13 changed files with 155 additions and 8 deletions

View File

@@ -22,6 +22,9 @@ Usage:
sign <sequence> <validator-public> <master-secret>
Create a new signed manifest with the given sequence
number, validator public key, and master secret key.
verify <sequence> <validator-public> <signature> <master-public>
Verify hex-encoded manifest signature with master public key.
"""
def prepend_length_byte(b):
@@ -113,6 +116,16 @@ def get_signature(seq, validator_public_key_human, private_key_human):
m1 = sign_manifest(m, private_key, pk)
return base64.b64encode(m1)
def verify_signature(seq, validator_public_key_human, public_key_human, signature):
v, validator_public_key = Base58.decode_version(validator_public_key_human)
check_validator_public(v, validator_public_key)
v, public_key = Base58.decode_version(public_key_human)
m = make_manifest(public_key, validator_public_key, seq)
public_key = public_key[1:] # Remove ED25519_BYTE
sig = signature.decode('hex')
ed25519.checkvalid(sig, 'MAN\0' + m, public_key)
# Testable versions of functions.
def perform_create(urandom=os.urandom, print=print):
@@ -131,6 +144,12 @@ def perform_sign(
print(wrap(get_signature(
int(seq), validator_public_key_human, private_key_human)))
def perform_verify(
seq, validator_public_key_human, public_key_human, signature, print=print):
verify_signature(
int(seq), validator_public_key_human, public_key_human, signature)
print('Signature valid for', public_key_human)
# Externally visible versions of functions.
def create():
perform_create()
@@ -141,6 +160,8 @@ def check(s):
def sign(seq, validator_public_key_human, private_key_human):
perform_sign(seq, validator_public_key_human, private_key_human)
def verify(seq, validator_public_key_human, public_key_human, signature):
perform_verify(seq, validator_public_key_human, public_key_human, signature)
def usage(*errors):
if errors:
@@ -148,7 +169,7 @@ def usage(*errors):
print(USAGE)
return not errors
_COMMANDS = dict((f.__name__, f) for f in (create, check, sign))
_COMMANDS = dict((f.__name__, f) for f in (create, check, sign, verify))
def run_command(args):
if not args:

View File

@@ -14,6 +14,10 @@ class test_Sign(TestCase):
'JAAAABdxIe2DIKUZd9jDjKikknxnDfWCHkSXYZReFenvsmoVCdIw6nMhAnZ2dnZ2'
'dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dkDOjlWtQSvRTjuwe+4iNusg0sJM'
'zqkBJwDz30b2SkxZ7Fte/Vx4htM/kkfUfJCaxmxE5N4dHSKuiO9iDHsktqIA')
VALIDATOR_KEY_HUMAN = 'n9JijuoCv8ubEy5ag3LiX3hyq27GaLJsitZPbQ6APkwx2MkUXq8E'
SIGNATURE_HEX = (
'0a1546caa29c887f9fcb5e6143ea101b31fb5895a5cdfa24939301c66ff51794'
'a0b729e0ebbf576f2cc7cdb9f68c2366324a53b8e1ecf16f3c17bebbdb8d7102')
def setUp(self):
self.results = []
@@ -96,6 +100,11 @@ class test_Sign(TestCase):
'dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dkDOjlWtQSvRTjuwe+4iNusg0sJM'
'zqkBJwDz30b2SkxZ7Fte/Vx4htM/kkfUfJCaxmxE5N4dHSKuiO9iDHsktqIA')
def test_verify_signature(self):
Sign.verify_signature(self.SEQUENCE, self.VALIDATOR_KEY_HUMAN,
'nHUUaKHpxyRP4TZZ79tTpXuTpoM8pRNs5crZpGVA5jdrjib5easY',
self.SIGNATURE_HEX)
def test_check(self):
public = Base58.encode_version(Base58.VER_NODE_PRIVATE, 32 * 'k')
Sign.perform_check(public, self.print)
@@ -125,3 +134,8 @@ class test_Sign(TestCase):
'Z2dnZ2dkDOjlWtQSvRTjuwe+4iNusg0sJMzqkBJwDz30b2S\n'
'kxZ7Fte/Vx4htM/kkfUfJCaxmxE5N4dHSKuiO9iDHsktqIA'],
{}]])
def test_verify(self):
Sign.perform_verify(self.SEQUENCE, self.VALIDATOR_KEY_HUMAN,
'nHUUaKHpxyRP4TZZ79tTpXuTpoM8pRNs5crZpGVA5jdrjib5easY',
self.SIGNATURE_HEX, print=self.print)

View File

@@ -436,6 +436,10 @@ public:
bool subBook (InfoSub::ref ispListener, Book const&) override;
bool unsubBook (std::uint64_t uListener, Book const&) override;
bool subManifests (InfoSub::ref ispListener) override;
bool unsubManifests (std::uint64_t uListener) override;
void pubManifest (Manifest const&) override;
bool subTransactions (InfoSub::ref ispListener) override;
bool unsubTransactions (std::uint64_t uListener) override;
@@ -526,6 +530,7 @@ private:
subRpcMapType mRpcSubMap;
SubMapType mSubLedger; // Accepted ledgers.
SubMapType mSubManifests; // Received validator manifests.
SubMapType mSubServer; // When server changes connectivity state.
SubMapType mSubTransactions; // All accepted transactions.
SubMapType mSubRTTransactions; // All proposed and accepted transactions.
@@ -1507,6 +1512,36 @@ void NetworkOPsImp::consensusViewChange ()
setMode (omCONNECTED);
}
void NetworkOPsImp::pubManifest (Manifest const& mo)
{
// VFALCO consider std::shared_mutex
ScopedLockType sl (mSubLock);
if (!mSubManifests.empty ())
{
Json::Value jvObj (Json::objectValue);
jvObj [jss::type] = "manifestReceived";
jvObj [jss::master_key] = toBase58(TokenType::TOKEN_NODE_PUBLIC, mo.masterKey);
jvObj [jss::signing_key] = toBase58(TokenType::TOKEN_NODE_PUBLIC, mo.signingKey);
jvObj [jss::seq] = Json::UInt (mo.sequence);
jvObj [jss::signature] = strHex (mo.getSignature ());
for (auto i = mSubManifests.begin (); i != mSubManifests.end (); )
{
if (auto p = i->second.lock())
{
p->send (jvObj, true);
++i;
}
else
{
i = mSubManifests.erase (i);
}
}
}
}
void NetworkOPsImp::pubServer ()
{
// VFALCO TODO Don't hold the lock across calls to send...make a copy of the
@@ -1568,9 +1603,7 @@ void NetworkOPsImp::pubValidation (STValidation::ref val)
for (auto i = mSubValidations.begin (); i != mSubValidations.end (); )
{
InfoSub::pointer p = i->second.lock ();
if (p)
if (auto p = i->second.lock())
{
p->send (jvObj, true);
++i;
@@ -2538,6 +2571,20 @@ bool NetworkOPsImp::unsubLedger (std::uint64_t uSeq)
return mSubLedger.erase (uSeq);
}
// <-- bool: true=added, false=already there
bool NetworkOPsImp::subManifests (InfoSub::ref isrListener)
{
ScopedLockType sl (mSubLock);
return mSubManifests.emplace (isrListener->getSeq (), isrListener).second;
}
// <-- bool: true=erased, false=was not there
bool NetworkOPsImp::unsubManifests (std::uint64_t uSeq)
{
ScopedLockType sl (mSubLock);
return mSubManifests.erase (uSeq);
}
// <-- bool: true=added, false=already there
bool NetworkOPsImp::subServer (InfoSub::ref isrListener, Json::Value& jvResult,
bool admin)

View File

@@ -22,6 +22,7 @@
#include <ripple/basics/CountedObject.h>
#include <ripple/json/json_value.h>
#include <ripple/overlay/impl/Manifest.h>
#include <ripple/resource/Consumer.h>
#include <ripple/protocol/Book.h>
#include <beast/threads/Stoppable.h>
@@ -84,6 +85,10 @@ public:
virtual bool subLedger (ref ispListener, Json::Value& jvResult) = 0;
virtual bool unsubLedger (std::uint64_t uListener) = 0;
virtual bool subManifests (ref ispListener) = 0;
virtual bool unsubManifests (std::uint64_t uListener) = 0;
virtual void pubManifest (Manifest const&) = 0;
virtual bool subServer (ref ispListener, Json::Value& jvResult,
bool admin) = 0;
virtual bool unsubServer (std::uint64_t uListener) = 0;

View File

@@ -56,6 +56,7 @@ InfoSub::~InfoSub ()
m_source.unsubTransactions (mSeq);
m_source.unsubRTTransactions (mSeq);
m_source.unsubLedger (mSeq);
m_source.unsubManifests (mSeq);
m_source.unsubServer (mSeq);
m_source.unsubValidations (mSeq);
m_source.unsubPeerStatus (mSeq);

View File

@@ -119,6 +119,14 @@ bool Manifest::revoked () const
return sequence == std::numeric_limits<std::uint32_t>::max ();
}
Blob Manifest::getSignature () const
{
STObject st (sfGeneric);
SerialIter sit (serialized.data (), serialized.size ());
st.set (sit);
return st.getFieldVL (sfSignature);
}
void
ManifestCache::configValidatorKey(
std::string const& line, beast::Journal journal)

View File

@@ -98,6 +98,7 @@ struct Manifest
bool verify () const;
uint256 hash () const;
bool revoked () const;
Blob getSignature () const;
};
boost::optional<Manifest> make_Manifest(std::string s);

View File

@@ -19,6 +19,7 @@
#include <BeastConfig.h>
#include <ripple/app/misc/HashRouter.h>
#include <ripple/app/misc/NetworkOPs.h>
#include <ripple/core/DatabaseCon.h>
#include <ripple/basics/contract.h>
#include <ripple/basics/Log.h>
@@ -695,6 +696,10 @@ OverlayImpl::onManifests (
app_.validators(),
journal);
if (result == ManifestDisposition::accepted ||
result == ManifestDisposition::untrusted)
app_.getOPs().pubManifest (*make_Manifest(serialized));
if (result == ManifestDisposition::accepted)
{
auto db = app_.getWalletDB ().checkoutDb ();

View File

@@ -19,6 +19,7 @@
#include <BeastConfig.h>
#include <ripple/basics/contract.h>
#include <ripple/basics/StringUtilities.h>
#include <ripple/basics/TestSuite.h>
#include <ripple/overlay/impl/Manifest.h>
#include <ripple/core/DatabaseCon.h>
@@ -93,9 +94,9 @@ public:
auto const pk = derivePublicKey(type, sk);
STObject st(sfGeneric);
set(st, sfSequence, seq);
set(st, sfPublicKey, pk);
set(st, sfSigningPubKey, spk);
st[sfSequence] = seq;
st[sfPublicKey] = pk;
st[sfSigningPubKey] = spk;
sign(st, HashPrefix::manifest, type, sk);
expect(verify(st, HashPrefix::manifest, pk, true));
@@ -179,6 +180,26 @@ public:
boost::filesystem::path (dbName));
}
void testGetSignature()
{
testcase ("getSignature");
auto const sk = randomSecretKey();
auto const pk = derivePublicKey(KeyType::ed25519, sk);
auto const kp = randomKeyPair(KeyType::secp256k1);
auto const m = make_Manifest (KeyType::ed25519, sk, kp.first, 0);
STObject st(sfGeneric);
st[sfSequence] = 0;
st[sfPublicKey] = pk;
st[sfSigningPubKey] = kp.first;
Serializer ss;
ss.add32(HashPrefix::manifest);
st.addWithoutSigningFields(ss);
auto const sig = sign(KeyType::ed25519, sk, ss.slice());
expect (strHex(sig) == strHex(m.getSignature()));
}
void
run() override
{
@@ -227,6 +248,7 @@ public:
expect (cache.applyManifest (clone (s_b2), *unl, journal) == invalid);
}
testLoadStore (cache, *unl);
testGetSignature ();
}
};

View File

@@ -341,6 +341,7 @@ JSS ( server_state ); // out: NetworkOPs
JSS ( server_status ); // out: NetworkOPs
JSS ( severity ); // in: LogLevel
JSS ( signature ); // out: NetworkOPs
JSS ( signing_key ); // out: NetworkOPs
JSS ( signer_list ); // in: AccountObjects
JSS ( snapshot ); // in: Subscribe
JSS ( source_account ); // in: PathRequest, RipplePathFind

View File

@@ -124,6 +124,10 @@ Json::Value doSubscribe (RPC::Context& context)
{
context.netOps.subLedger (ispSub, jvResult);
}
else if (streamName == "manifests")
{
context.netOps.subManifests (ispSub);
}
else if (streamName == "transactions")
{
context.netOps.subTransactions (ispSub);

View File

@@ -77,6 +77,10 @@ Json::Value doUnsubscribe (RPC::Context& context)
{
context.netOps.unsubLedger (ispSub->getSeq ());
}
else if (streamName == "manifests")
{
context.netOps.unsubManifests (ispSub->getSeq ());
}
else if (streamName == "transactions")
{
context.netOps.unsubTransactions (ispSub->getSeq ());

View File

@@ -170,6 +170,21 @@ suite('JSON-RPC', function() {
});
});
test('subscribe manifests', function(done) {
var rippled_config = testutils.get_server_config(config);
var client = jsonrpc.client("http://" + rippled_config.rpc_ip + ":" + rippled_config.rpc_port);
var http_config = config.http_servers["zed"];
client.call('subscribe', [{
'url' : "http://" + http_config.ip + ":" + http_config.port,
'streams' : [ 'manifests' ],
}], function (result) {
assert(typeof result === 'object');
assert(result.status === 'success');
done();
});
});
test('subscribe validations', function(done) {
var rippled_config = testutils.get_server_config(config);
var client = jsonrpc.client("http://" + rippled_config.rpc_ip + ":" + rippled_config.rpc_port);
@@ -179,7 +194,6 @@ suite('JSON-RPC', function() {
'url' : "http://" + http_config.ip + ":" + http_config.port,
'streams' : [ 'validations' ],
}], function (result) {
// console.log(JSON.stringify(result, undefined, 2));
assert(typeof result === 'object');
assert(result.status === 'success');
done();