Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial implementation #7

Open
wants to merge 22 commits into
base: master
Choose a base branch
from
Open

Initial implementation #7

wants to merge 22 commits into from

Conversation

christosporios
Copy link
Member

@christosporios christosporios commented May 29, 2017

This PR implements the following:

  • Transaction parsing: All trust transactions seen by the node are translated into direct trusts.
  • Maximum Flow Trust Inference: Indirect trust can be calculated between any two entities.
  • Transaction Creation: Trust increasing, trust decreasing and trust stealing transaction can be created.

Unit and E2E tests for the above are included.

@christosporios christosporios mentioned this pull request May 29, 2017
@christosporios christosporios mentioned this pull request May 29, 2017
@dionyziz
Copy link
Member

dionyziz commented Jun 1, 2017

type annotations in tests

"onlyFilesWithFlowAnnotation": true
}
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit

.travis.yml Outdated
script:
- npm run lint
- npm run typecheck
- npm test
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

npm run check

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@@ -25,11 +27,19 @@
},
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MIT

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

test/helpers.js Outdated
return node.getBlock(node.chain.tip.hash);
},

time: async (milliseconds) => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rename to delay

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

test/helpers.js Outdated
"0503054CF7EBB4E62191AF1D8DE97945178D3F465EE88EF1FB4E80A70CB4A49A",
"878DFE5B43AC858EA37B3A9EEBA9E244F1848A30F78B2E5AC5B3EBDE81AC7D45",
"1349A1318B1426E6F724CBFE7ECD2C46008A364A96C4BD20C83FC1C4EBB2EB4A"
].map((key) => KeyRing.fromPrivate(new Buffer(key, "hex"))),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move to fixture file

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

var trustIncrease = this.parseTXAsTrustIncrease(tx);
if (trustIncrease !== null) {
return [trustIncrease];
} else {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

avoid else-after-return

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i also wonder why programmers do this

Copy link
Member Author

@christosporios christosporios Jul 11, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above. Throughout the code I omit the else only when it doesn't help convey the meaning of the code, for example when guarding against error cases. Here there's two equally valid cases: either it's a trust increase or it's not.

@gubatron your comment is not very constructive.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK

]
});

var changeAmount = coin.value - trustAmount - fee;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

assert(changeAmount >= 0)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why? It's okay to have no change.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assert that change is not negative

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

var mtx = new MTX({
outputs: [
new Output({
script: bcoin.script.fromMultisig(1, 2, [originPubKey, dest]),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

check that origin != dest

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

var directTrusts = this.db.getSpendableDirectTrusts(originAddress, destAddress);
return directTrusts.map((directTrust) => {
var decrease = Math.min(trustDecreaseAmount, directTrust.amount);
if (decrease === 0) return null;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps a for loop with a break here would be more readable, also avoiding the filter(Boolean) part – and as a side effect it will also avoid unnecessary iterations.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm having trouble making flow type the resulting array correctly when I do that. Will investigate further.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apparently flow isn't that smart, can't get anything other than filter(Boolean) to work.

],
outputs: [new Output({
script: bcoin.script.fromPubkeyhash(Address.fromBase58(payee).hash),
value: decreaseAmount - fee
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check that this is positive. If not, make this zero? Throwing an exception may not be appropriate: If multiple txs are spent to achieve the decrease, the last one may be borderline below the fee.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

src/trust_db.js Outdated
}

getDirectTrustByOutpoint(outpoint : bcoin$Outpoint) : (DirectTrust | null) {
var trust = this.txToDirectTrust.get(outpoint.hash.toString("hex"));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use getDirectTrustByTx

return trusts;
}

getGraphWeightMatrix() : number[][] {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If more readable, then move graphWeightMatrix to class variable and updated it when add is called.

if (tx.inputs.length !== 1) return null;
var input = tx.inputs[0];
if (input.getType() !== "pubkeyhash") return null; // TODO: This is unreliable
if (this.db.isTrustOutput(input.prevout.hash.toString("hex"), input.prevout.index)) return null;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This condition is unnecessary, because if null is returned here, then also null is returned in line 191.

var input = tx.inputs[0];
if (input.getType() !== "pubkeyhash") return null; // TODO: This is unreliable
if (this.db.isTrustOutput(input.prevout.hash.toString("hex"), input.prevout.index)) return null;
var origin = tx.inputs[0].getAddress().toBase58();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Explain the cases this getAddress can return (p2pkh or something else) in a comment

if (this.db.isTrustOutput(input.prevout.hash.toString("hex"), input.prevout.index)) return null;
var origin = tx.inputs[0].getAddress().toBase58();

if (tx.outputs.length === 0 || tx.outputs.length > 2) return null;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unnecessary

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It breaks the following test if I remove it, which makes sense:

.addTX() with a trust increasing transaction which has two change outputs does not change trust

Are you saying that we could relax this condition and allow trust increasing transactions with more than one change outputs?

).filter(Boolean);

if (recipient) {
directTrusts.filter((trust) => trust.dest === recipient);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

filter doesn't modify underlying array – please add more regression tests

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, this was a major bug. I've added a test to protect against it and fixed the problem.


getInputTrusts(inputs : bcoin$Input[]) : DirectTrust[] {
return inputs.map((input) => {
var trust = this.db.getDirectTrustByOutpoint(input.prevout);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Think about & create github issue about malleability

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

getInputTrusts(inputs : bcoin$Input[]) : DirectTrust[] {
return inputs.map((input) => {
var trust = this.db.getDirectTrustByOutpoint(input.prevout);
if (trust && trust.outputIndex === input.prevout.index) return trust;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

trust.outputIndex === input.prevout.index unnecessary

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

return inputs.map((input) => {
var trust = this.db.getDirectTrustByOutpoint(input.prevout);
if (trust && trust.outputIndex === input.prevout.index) return trust;
else return null;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can just return trust;

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, done.


nextTrust.prev = prevTrust;

assert(nextTrust.amount - prevTrust.amount <= 0);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

more readable as nextTrust.amount <= prevTrust.amount

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@christosporios christosporios dismissed dionyziz’s stale review September 11, 2017 23:36

Some minor change requests left. Opened issue #14.

@christosporios
Copy link
Member Author

Most CR comments have been addressed, let's merge this ASAP so that Orfeas can continue. The remaining comments are mostly readability related and I've opened #14 for them.

}

getTrustDecreasingMTX(directTrust : DirectTrust, decreaseAmount : number, payee : ?Entity,
signingKeyRing : bcoin$KeyRing, fee : ?number) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add return type
: bcoin$MTX

@OrfeasLitos OrfeasLitos mentioned this pull request Sep 22, 2017
"extends": [
"eslint:recommended",
"plugin:flowtype/recommended"
],
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: indentation issues

@@ -0,0 +1,40 @@
{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replace tabs with spaces in file

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants