Project { public CompletableFuture<LHProtos.Pledge> verifyPledge(UTXOSource peer, LHProtos.Pledge pledge) { Sha256Hash pledgeHash = LHUtils.hashFromPledge(pledge); try { log.debug("Checking pledge {} for project '{}' [{}]", pledgeHash, getTitle(), getID()); Transaction tx = fastSanityCheck(pledge); return lookupUTXOs(peer, tx).thenApply((result) -> { if (result.size() != tx.getInputs().size()) throw new Ex.UnknownUTXO(); verifyScripts(tx, result); verifyValues(pledge, result); log.info("Pledge {} appears to be OK", pledgeHash); return pledge; }); } catch (Exception e) { log.error("Pledge is invalid: {}: {}", pledgeHash, Throwables.getRootCause(e)); CompletableFuture<LHProtos.Pledge> future = new CompletableFuture<>(); future.completeExceptionally(e); return future; } } Project(LHProtos.ProjectDetails details);  Project(LHProtos.Project proto); static LHProtos.ProjectDetails.Builder makeDetails(NetworkParameters params, String title, String memo, Address to, Coin value, DeterministicKey authKey, int lookaheadSize); LHProtos.Project getProto(); LHProtos.ProjectDetails getProtoDetails(); String getID(); Sha256Hash getIDHash(); boolean isServerAssisted(); String getTitle(); String getEmail(); String getMemo(); Coin getGoalAmount(); ByteString getCoverImage(); List<TransactionOutput> getOutputs(); Instant getExpires(); boolean isExpired(); @Nullable URI getPaymentURL(); CompletableFuture<LHProtos.Pledge> verifyPledge(UTXOSource peer, LHProtos.Pledge pledge); Transaction fastSanityCheck(LHProtos.Pledge pledge); Transaction completeContract(Set<LHProtos.Pledge> pledges); CompletableFuture<LHProtos.ProjectStatus> getStatus(PledgingWallet wallet, @Nullable KeyParameter key); @Override String toString(); @Override boolean equals(Object o); @Override int hashCode(); String getSuggestedFileName(); static String getSuggestedFileName(String title); Coin getMinPledgeAmount(); static Coin getMinPledgeAmountFrom(long minPledgeAmount); @Nullable String signAsOwner(PledgingWallet wallet, String message, @Nullable KeyParameter aesKey); void authenticateOwner(String message, String signatureBase64); byte[] getAuthKey(); int getAuthKeyIndex(); NetworkParameters getParams(); static final int COVER_IMAGE_WIDTH; static final int COVER_IMAGE_HEIGHT; static String GET_STATUS_USER_AGENT; }
@Test(expected = Ex.NoTransactionData.class) public void badPledgeNoTx() throws Exception { LHProtos.Pledge.Builder pledge = LHProtos.Pledge.newBuilder(); pledge.getPledgeDetailsBuilder().setTotalInputValue(0); pledge.getPledgeDetailsBuilder().setProjectId("abc"); pledge.getPledgeDetailsBuilder().setTimestamp(Utils.currentTimeSeconds()); pledge.getPledgeDetailsBuilder(); Project project = new Project(projectBuilder.build()); checkedGet(project.verifyPledge(outPoints -> completedFuture(EMPTY_LIST), pledge.build())); }
@Test(expected = Ex.OutputMismatch.class) public void badPledgeWrongOutput() throws Exception { TxData pledgeTX = makePledge(details, 0.5); pledgeTX.pledge.getOutput(0).setValue(Coin.valueOf(100)); LHProtos.Pledge.Builder pledge = LHProtos.Pledge.newBuilder(); pledge.addTransactions(ByteString.copyFrom(pledgeTX.pledge.bitcoinSerialize())); pledge.getPledgeDetailsBuilder().setTotalInputValue(0); pledge.getPledgeDetailsBuilder().setTimestamp(Utils.currentTimeSeconds()); pledge.getPledgeDetailsBuilder().setProjectId("abc"); pledge.getPledgeDetailsBuilder(); Project project = new Project(projectBuilder.build()); checkedGet(project.verifyPledge(outPoints -> completedFuture(EMPTY_LIST), pledge.build())); }
@Test(expected = Ex.UnknownUTXO.class) public void missingUTXO() throws Exception { TxData pledgeTX = makePledge(details, 0.5); LHProtos.Pledge.Builder pledge = LHProtos.Pledge.newBuilder(); pledge.addTransactions(ByteString.copyFrom(pledgeTX.pledge.bitcoinSerialize())); pledge.getPledgeDetailsBuilder().setTotalInputValue(0); pledge.getPledgeDetailsBuilder().setTimestamp(Utils.currentTimeSeconds()); pledge.getPledgeDetailsBuilder().setProjectId("abc"); pledge.getPledgeDetailsBuilder(); Project project = new Project(projectBuilder.build()); checkedGet(project.verifyPledge(outPoints -> completedFuture(EMPTY_LIST), pledge.build())); }
@Test(expected = ScriptException.class) public void badSignature() throws Exception { TxData pledgeTX = makePledge(details, 0.1); pledgeTX.pledge.getInput(0).setScriptSig(ScriptBuilder.createInputScript(TransactionSignature.dummy(), pledgeTX.key)); LHProtos.Pledge.Builder pledge = pledgeToBuilder(pledgeTX, false); Project project = new Project(projectBuilder.build()); List<TransactionOutput> outputs = ImmutableList.of(pledgeTX.fakeStub.getOutput(0).duplicateDetached()); checkedGet(project.verifyPledge(outPoints -> completedFuture(outputs), pledge.build())); }
@Test(expected = VerificationException.class) public void badOutputScript() throws Exception { TxData pledgeTX = makePledge(details, 0.1); LHProtos.Pledge.Builder pledge = LHProtos.Pledge.newBuilder(); pledge.addTransactions(ByteString.copyFrom(pledgeTX.pledge.bitcoinSerialize())); final Coin val = Coin.COIN.divide(10); pledge.getPledgeDetailsBuilder().setTotalInputValue(val.value); pledge.getPledgeDetailsBuilder().setTimestamp(Utils.currentTimeSeconds()); pledge.getPledgeDetailsBuilder().setProjectId("abc"); Project project = new Project(projectBuilder.build()); TransactionOutput badOutput = new TransactionOutput(params, null, val, new ScriptBuilder().op(ScriptOpCodes.OP_TRUE).build().getProgram()); List<TransactionOutput> outputs = ImmutableList.of(badOutput); checkedGet(project.verifyPledge(outPoints -> completedFuture(outputs), pledge.build())); }
@Test(expected = Ex.CachedValueMismatch.class) public void badTotalValueField() throws Exception { TxData pledgeTX = makePledge(details, 0.1); LHProtos.Pledge.Builder pledge = pledgeToBuilder(pledgeTX, true); Project project = new Project(projectBuilder.build()); List<TransactionOutput> outputs = ImmutableList.of(pledgeTX.fakeStub.getOutput(0).duplicateDetached()); checkedGet(project.verifyPledge(outPoints -> completedFuture(outputs), pledge.build())); }
@Test(expected = Ex.NonStandardInput.class) public void nonStandardInputRegularOutput() throws Exception { TxData pledgeTX = makePledge(details, 0.1); TransactionInput input = pledgeTX.pledge.getInput(0); input.setScriptSig( new ScriptBuilder(input.getScriptSig()) .data(0, new byte[]{}) .build() ); LHProtos.Pledge.Builder pledge = pledgeToBuilder(pledgeTX, false); Project project = new Project(projectBuilder.build()); List<TransactionOutput> outputs = ImmutableList.of(pledgeTX.fakeStub.getOutput(0).duplicateDetached()); checkedGet(project.verifyPledge(outPoints -> completedFuture(outputs), pledge.build())); }
@Test(expected = Ex.NonStandardInput.class) public void nonStandardInputP2SHOutput() throws Exception { TxData pledgeTX = makePledge(details, 0.1); TransactionInput input = pledgeTX.pledge.getInput(0); TransactionOutput origOutput = pledgeTX.fakeStub.getOutput(0); Script p2shPK = ScriptBuilder.createP2SHOutputScript(origOutput.getScriptPubKey()); Script p2shSS = new ScriptBuilder(input.getScriptSig()).data(origOutput.getScriptBytes()).data(0, new byte[]{}).build(); input.setScriptSig(p2shSS); pledgeTX.fakeStub.clearOutputs(); pledgeTX.fakeStub.addOutput(origOutput.getValue(), p2shPK); LHProtos.Pledge.Builder pledge = pledgeToBuilder(pledgeTX, false); Project project = new Project(projectBuilder.build()); List<TransactionOutput> outputs = ImmutableList.of(pledgeTX.fakeStub.getOutput(0).duplicateDetached()); checkedGet(project.verifyPledge(outPoints -> completedFuture(outputs), pledge.build())); }
@Test public void okPledge() throws Exception { TxData pledgeTX = makePledge(details, 0.1); LHProtos.Pledge.Builder pledge = pledgeToBuilder(pledgeTX, false); Project project = new Project(projectBuilder.build()); List<TransactionOutput> outputs = ImmutableList.of(pledgeTX.fakeStub.getOutput(0).duplicateDetached()); checkedGet(project.verifyPledge(outPoints -> completedFuture(outputs), pledge.build())); }