diff --git a/src/contracts/CrowdFund.kt b/src/contracts/CrowdFund.kt
new file mode 100644
index 0000000000..355929bb8f
--- /dev/null
+++ b/src/contracts/CrowdFund.kt
@@ -0,0 +1,175 @@
+package contracts
+
+import core.*
+import core.serialization.SerializeableWithKryo
+import java.security.PublicKey
+import java.time.Instant
+import java.util.*
+
+val CROWDFUND_PROGRAM_ID = SecureHash.sha256("crowdsourcing")
+
+/**
+ * This is a basic crowd funding contract. It allows a party to create a funding opportunity, then for others to
+ * pledge during the funding period , and then for the party to either accept the funding (if the target has been reached)
+ * return the funds to the pledge-makers (if the target has not been reached)
+ */
+class CrowdFund : Contract {
+
+    data class State(
+            val owner: PublicKey,
+            val fundingName: String,
+            val fundingTarget: Amount,
+            val closingTime: Instant,
+            val closed: Boolean = false,
+            val pledgeTotal: Amount = 0.DOLLARS,
+            val pledgeCount: Int = 0,
+            val pledges: List<Pledge> = ArrayList()
+    ) : ContractState {
+        override val programRef = CROWDFUND_PROGRAM_ID
+        override fun toString() = "Crowdsourcing($fundingTarget sought by $owner by $closingTime)"
+    }
+
+    data class Pledge (
+            val owner: PublicKey,
+            val amount: Amount
+    ) : SerializeableWithKryo
+
+
+    interface Commands : Command {
+        object Register : Commands
+        object Fund : Commands
+        object Funded : Commands
+        object Unfunded : Commands
+    }
+
+    override fun verify(tx: TransactionForVerification) {
+    // There are two possible things that can be done with Crowdsourcing.
+    // The first is creating it. The second is funding it with cash
+    // The third is closing it on or after the closing date, and returning funds to
+    // pledge-makers if the target is unmet, or passing to the recipient.
+    val command = tx.commands.requireSingleCommand<CrowdFund.Commands>()
+
+        val outputCrowdFund: CrowdFund.State = tx.outStates.filterIsInstance<CrowdFund.State>().single()
+        val outputCash: List<Cash.State> = tx.outStates.filterIsInstance<Cash.State>()
+
+    when (command.value) {
+            is Commands.Register -> {
+                requireThat {
+                    "the transaction is signed by the owner of the crowdsourcing" by (command.signers.contains(outputCrowdFund.owner))
+                    "the output registration is empty of pledges" by (outputCrowdFund.pledges.isEmpty())
+                    "the output registration has a non-zero target" by (outputCrowdFund.fundingTarget.pennies > 0)
+                    "the output registration has a zero starting pledge total" by (outputCrowdFund.pledgeTotal.pennies == 0)
+                    "the output registration has a zero starting pledge count" by (outputCrowdFund.pledgeCount == 0)
+                    "the output registration has a funding currency" by (outputCrowdFund.pledgeTotal.currency.currencyCode.isNotBlank()) // TODO is this necessary? currency is not nullable
+                    "the output registration has a name" by (outputCrowdFund.fundingName.isNotBlank())
+                    "the output registration has a closing time in the future" by (outputCrowdFund.closingTime > tx.time)
+                    "the output registration has an open state" by (!outputCrowdFund.closed)
+                }
+            }
+
+            is Commands.Fund -> {
+                val inputCrowdFund: CrowdFund.State = tx.inStates.filterIsInstance<CrowdFund.State>().single()
+                val inputCash: List<Cash.State> = tx.inStates.filterIsInstance<Cash.State>()
+                val pledge = outputCrowdFund.pledges.last()
+                val pledgedCash = outputCash.single()
+                requireThat {
+                    "the funding is still open" by (inputCrowdFund.closingTime >= tx.time)
+                    // TODO  "the transaction is signed by the owner of the pledge" by (command.signers.contains(inputCrowdFund.owner))
+                    "the transaction is signed by the pledge-maker" by (command.signers.contains(pledge.owner))
+                    "the pledge must be for a non-zero amount" by (pledge.amount.pennies > 0)
+                    "the pledge must be in the same currency as the goal" by (pledge.amount.currency == outputCrowdFund.fundingTarget.currency)
+                    "the number of pledges must have increased by one" by (outputCrowdFund.pledgeCount == inputCrowdFund.pledgeCount + 1)
+                    "the pledged total has increased by the value of the pledge" by (outputCrowdFund.pledgeTotal.pennies == inputCrowdFund.pledgeTotal.pennies + inputCash.sumCash().pennies)
+                    "the pledge has been added to the list of pledges" by (outputCrowdFund.pledges.size == outputCrowdFund.pledgeCount)
+                    "the cash input has been assigned to the funding owner" by (pledgedCash.owner == inputCrowdFund.owner)
+                    // TODO how to simplify the boilerplate associated with unchanged elements
+                    "the owner hasn't changed" by (outputCrowdFund.owner == inputCrowdFund.owner)
+                    "the funding name has not changed" by (outputCrowdFund.fundingName == inputCrowdFund.fundingName)
+                    "the funding target has not changed" by (outputCrowdFund.fundingTarget == inputCrowdFund.fundingTarget)
+                    "the closing time has not changed" by (outputCrowdFund.closingTime == inputCrowdFund.closingTime)
+                    "the pledged total currency is unchanged" by (outputCrowdFund.pledgeTotal.currency == inputCrowdFund.pledgeTotal.currency)
+                    "the output registration has an open state" by (!outputCrowdFund.closed)
+                }
+            }
+
+        is Commands.Unfunded -> {
+            val inputCrowdFund: CrowdFund.State = tx.inStates.filterIsInstance<CrowdFund.State>().single()
+            // TODO how can this be made smarter? feels wrong as a separate function
+            fun checkReturns(inputCrowdFund: CrowdFund.State, outputCash: List<Cash.State>): Boolean {
+                for (pledge in inputCrowdFund.pledges) {
+                    if (outputCash.filter { it.amount == pledge.amount && it.owner == pledge.owner}.isEmpty()) return false
+                }
+                return true
+            }
+
+            requireThat {
+                "the closing date has past" by (tx.time >= outputCrowdFund.closingTime)
+                "the pledges did not meet the target" by (inputCrowdFund.pledgeTotal < inputCrowdFund.fundingTarget)
+                "the output cash returns equal the pledge total, if the target is not reached" by (outputCash.sumCash() == inputCrowdFund.pledgeTotal)
+                "the output cash is distributed to the pledge-makers, if the target is not reached" by (checkReturns(inputCrowdFund, outputCash))
+                "the output cash is distributed to the pledge-makers, if the target is not reached" by (outputCash.map { it.amount }.containsAll(inputCrowdFund.pledges.map { it.amount }))
+                "the input has an open state" by (!inputCrowdFund.closed)
+                "the output registration has a closed state" by (outputCrowdFund.closed)
+                // TODO how to simplify the boilerplate associated with unchanged elements
+                "the owner hasn't changed" by (outputCrowdFund.owner == inputCrowdFund.owner)
+                "the funding name has not changed" by (outputCrowdFund.fundingName == inputCrowdFund.fundingName)
+                "the funding target has not changed" by (outputCrowdFund.fundingTarget == inputCrowdFund.fundingTarget)
+                "the closing time has not changed" by (outputCrowdFund.closingTime == inputCrowdFund.closingTime)
+                "the pledged total is unchanged" by (outputCrowdFund.pledgeTotal == inputCrowdFund.pledgeTotal)
+                "the pledged count is unchanged" by (outputCrowdFund.pledgeCount == inputCrowdFund.pledgeCount)
+                "the pledges are unchanged" by (outputCrowdFund.pledges == inputCrowdFund.pledges)
+            }
+        }
+
+        is Commands.Funded -> {
+            val inputCrowdFund: CrowdFund.State = tx.inStates.filterIsInstance<CrowdFund.State>().single()
+             requireThat {
+                "the closing date has past" by (tx.time >= outputCrowdFund.closingTime)
+                "the input has an open state" by (!inputCrowdFund.closed)
+                "the output registration has a closed state" by (outputCrowdFund.closed)
+                // TODO how to simplify the boilerplate associated with unchanged elements
+                "the owner hasn't changed" by (outputCrowdFund.owner == inputCrowdFund.owner)
+                "the funding name has not changed" by (outputCrowdFund.fundingName == inputCrowdFund.fundingName)
+                "the funding target has not changed" by (outputCrowdFund.fundingTarget == inputCrowdFund.fundingTarget)
+                "the closing time has not changed" by (outputCrowdFund.closingTime == inputCrowdFund.closingTime)
+                "the pledged total is unchanged" by (outputCrowdFund.pledgeTotal == inputCrowdFund.pledgeTotal)
+                "the pledged count is unchanged" by (outputCrowdFund.pledgeCount == inputCrowdFund.pledgeCount)
+                "the pledges are unchanged" by (outputCrowdFund.pledges == inputCrowdFund.pledges)
+            }
+        }
+
+    // TODO: Think about how to evolve contracts over time with new commands.
+            else -> throw IllegalArgumentException("Unrecognised command")
+        }
+    }
+
+    /**
+     * Returns a transaction that registers a crowd-funding campaing, owned by the issuing institution's key. Does not update
+     * an existing transaction because it's not possible to register multiple campaigns in a single transaction
+     */
+    fun craftRegister(owner: InstitutionReference, fundingTarget: Amount, fundingName: String, closingTime: Instant): PartialTransaction {
+        val state = State(owner = owner.institution.owningKey, fundingName = fundingName, fundingTarget = fundingTarget, closingTime = closingTime)
+        return PartialTransaction(state, WireCommand(CrowdFund.Commands.Register, owner.institution.owningKey))
+    }
+
+    /**
+     * Updates the given partial transaction with an input/output/command to fund the opportunity.
+     */
+    fun craftFund(tx: PartialTransaction, campaign: StateAndRef<State>, subscriber: PublicKey) {
+        tx.addInputState(campaign.ref)
+        tx.addOutputState(campaign.state.copy(
+                pledges = campaign.state.pledges + CrowdFund.Pledge(subscriber, 1000.DOLLARS),
+                pledgeCount = campaign.state.pledgeCount + 1,
+                pledgeTotal = campaign.state.pledgeTotal + 1000.DOLLARS
+        ))
+        tx.addArg(WireCommand(CrowdFund.Commands.Fund, subscriber))
+    }
+
+    fun craftFunded(tx: PartialTransaction, campaign: StateAndRef<State>) {
+        tx.addInputState(campaign.ref)
+        tx.addOutputState(campaign.state.copy(closed = true))
+        tx.addArg(WireCommand(CrowdFund.Commands.Funded, campaign.state.owner))
+    }
+
+    override val legalContractReference: SecureHash = SecureHash.sha256("Crowdsourcing")
+}
diff --git a/src/core/serialization/Kryo.kt b/src/core/serialization/Kryo.kt
index 04d184d38c..6773c4a031 100644
--- a/src/core/serialization/Kryo.kt
+++ b/src/core/serialization/Kryo.kt
@@ -8,6 +8,7 @@ import com.esotericsoftware.kryo.io.Output
 import com.esotericsoftware.kryo.serializers.JavaSerializer
 import contracts.Cash
 import contracts.CommercialPaper
+import contracts.CrowdFund
 import core.*
 import java.io.ByteArrayOutputStream
 import java.lang.reflect.InvocationTargetException
@@ -230,6 +231,12 @@ fun createKryo(): Kryo {
         register(CommercialPaper.Commands.Move.javaClass)
         register(CommercialPaper.Commands.Redeem.javaClass)
         register(CommercialPaper.Commands.Issue.javaClass)
+        // Added for
+        registerDataClass<CrowdFund.State>()
+        registerDataClass<CrowdFund.Pledge>()
+        register(CrowdFund.Commands.Register.javaClass)
+        register(CrowdFund.Commands.Fund.javaClass)
+        register(CrowdFund.Commands.Funded.javaClass)
 
         // And for unit testing ...
         registerDataClass<DummyPublicKey>()
diff --git a/tests/contracts/CrowdFundTests.kt b/tests/contracts/CrowdFundTests.kt
new file mode 100644
index 0000000000..9fbd6c5ef2
--- /dev/null
+++ b/tests/contracts/CrowdFundTests.kt
@@ -0,0 +1,132 @@
+package contracts
+
+import core.*
+import core.testutils.*
+import org.junit.Test
+import java.util.*
+
+class CrowdFundTests {
+    val CF_1 = CrowdFund.State(
+            owner = MINI_CORP_PUBKEY,
+            fundingName = "kickstart me",
+            fundingTarget = 1000.DOLLARS,
+            pledgeTotal = 0.DOLLARS,
+            pledgeCount = 0,
+            closingTime = TEST_TX_TIME + 7.days,
+            closed = false,
+            pledges = ArrayList<CrowdFund.Pledge>()
+    )
+
+    @Test
+    fun `key mismatch at issue`() {
+        transactionGroup {
+            transaction {
+                output { CF_1 }
+                arg(DUMMY_PUBKEY_1) { CrowdFund.Commands.Register }
+            }
+
+            expectFailureOfTx(1, "the transaction is signed by the owner of the crowdsourcing")
+        }
+    }
+
+    @Test
+    fun `closing time not in the future`() {
+        transactionGroup {
+            transaction {
+                output { CF_1.copy(closingTime = TEST_TX_TIME - 1.days) }
+                arg(MINI_CORP_PUBKEY) { CrowdFund.Commands.Register }
+            }
+
+            expectFailureOfTx(1, "the output registration has a closing time in the future")
+        }
+    }
+
+    @Test
+    fun ok() {
+        raiseFunds().verify()
+    }
+
+    private fun raiseFunds(): TransactionGroupForTest<CrowdFund.State> {
+        return transactionGroupFor<CrowdFund.State> {
+            roots {
+                transaction(1000.DOLLARS.CASH `owned by` ALICE label "alice's $1000")
+            }
+
+            // 1. Create the funding opportunity
+            transaction {
+                output("funding opportunity") { CF_1 }
+                arg(MINI_CORP_PUBKEY) { CrowdFund.Commands.Register }
+            }
+
+            // 2. Place a pledge
+            transaction {
+                input ("funding opportunity")
+                input("alice's $1000")
+                output ("pledged opportunity") { CF_1.copy(
+                        pledges = CF_1.pledges + CrowdFund.Pledge(ALICE, 1000.DOLLARS),
+                        pledgeCount = CF_1.pledgeCount + 1,
+                        pledgeTotal = CF_1.pledgeTotal + 1000.DOLLARS
+                ) }
+                output { 1000.DOLLARS.CASH `owned by` MINI_CORP_PUBKEY }
+                arg(ALICE) { Cash.Commands.Move }
+                arg(ALICE) { CrowdFund.Commands.Fund }
+            }
+
+            // 3. Close the opportunity, assuming the target has been met
+            transaction(TEST_TX_TIME + 8.days) {
+                input ("pledged opportunity")
+                output ("funded and closed") { "pledged opportunity".output.copy(closed = true) }
+                arg(MINI_CORP_PUBKEY) { CrowdFund.Commands.Funded }
+            }
+         }
+    }
+
+    fun cashOutputsToWallet(vararg states: Cash.State): Pair<LedgerTransaction, List<StateAndRef<Cash.State>>> {
+        val ltx = LedgerTransaction(emptyList(), listOf(*states), emptyList(), TEST_TX_TIME, SecureHash.randomSHA256())
+        return Pair(ltx, states.mapIndexed { index, state -> StateAndRef(state, ContractStateRef(ltx.hash, index)) })
+    }
+
+    @Test
+    fun `raise more funds`() {
+        // MiniCorp registers a crowdfunding of $1,000, to close in 30 days.
+        val registerTX: LedgerTransaction = run {
+            // craftRegister returns a partial transaction
+            val ptx = CrowdFund().craftRegister(MINI_CORP.ref(123), 1000.DOLLARS, "crowd funding", TEST_TX_TIME + 7.days)
+            ptx.signWith(MINI_CORP_KEY)
+            val stx = ptx.toSignedTransaction()
+            stx.verify().toLedgerTransaction(TEST_TX_TIME, TEST_KEYS_TO_CORP_MAP, SecureHash.randomSHA256())
+        }
+
+        // let's give Alice some funds that she can invest
+        val (aliceWalletTX, aliceWallet) = cashOutputsToWallet(
+                200.DOLLARS.CASH `owned by` ALICE,
+                500.DOLLARS.CASH `owned by` ALICE,
+                300.DOLLARS.CASH `owned by` ALICE
+        )
+
+        // Alice pays $1000 to MiniCorp to fund their campaign.
+        val pledgeTX: LedgerTransaction = run {
+            val ptx = PartialTransaction()
+            CrowdFund().craftFund(ptx, registerTX.outRef(0), ALICE)
+            Cash().craftSpend(ptx, 1000.DOLLARS, MINI_CORP_PUBKEY, aliceWallet)
+            ptx.signWith(ALICE_KEY)
+            val stx = ptx.toSignedTransaction()
+            // this verify passes - the transaction contains an output cash, necessary to verify the fund command
+            stx.verify().toLedgerTransaction(TEST_TX_TIME, TEST_KEYS_TO_CORP_MAP, SecureHash.randomSHA256())
+        }
+
+        // MiniCorp closes their campaign.
+        val fundedTX: LedgerTransaction = run {
+            val ptx = PartialTransaction()
+            CrowdFund().craftFunded(ptx, pledgeTX.outRef(0))
+            ptx.signWith(MINI_CORP_KEY)
+            val stx = ptx.toSignedTransaction()
+            stx.verify().toLedgerTransaction(TEST_TX_TIME + 8.days, TEST_KEYS_TO_CORP_MAP, SecureHash.randomSHA256())
+        }
+
+        // This verification passes
+        TransactionGroup(setOf(registerTX, pledgeTX, fundedTX), setOf(aliceWalletTX)).verify(TEST_PROGRAM_MAP)
+
+    }
+
+    }
\ No newline at end of file
diff --git a/tests/core/testutils/TestUtils.kt b/tests/core/testutils/TestUtils.kt
index faed264157..327aeba97e 100644
--- a/tests/core/testutils/TestUtils.kt
+++ b/tests/core/testutils/TestUtils.kt
@@ -44,6 +44,7 @@ val TEST_TX_TIME = Instant.parse("2015-04-17T12:00:00.00Z")
 val TEST_PROGRAM_MAP: Map<SecureHash, Contract> = mapOf(
         CASH_PROGRAM_ID to Cash(),
         CP_PROGRAM_ID to CommercialPaper(),
+        CROWDFUND_PROGRAM_ID to CrowdFund(),
         DUMMY_PROGRAM_ID to DummyContract
 )