Skip to content

Instantly share code, notes, and snippets.

@alikon
Created April 3, 2019 14:41
Show Gist options
  • Save alikon/279e509bb62d8e7a8f6fe090791be402 to your computer and use it in GitHub Desktop.
Save alikon/279e509bb62d8e7a8f6fe090791be402 to your computer and use it in GitHub Desktop.
draft ordine di pagamento state e contract
// TO DO da cambiare
package com.r3.businessnetworks.ordine.states
// TO DO da cambiare
import com.r3.businessnetworks.ordine.states.OrdineStatus.*
import net.corda.core.contracts.*
import net.corda.core.contracts.Requirements.using
import net.corda.core.identity.Party
import net.corda.core.schemas.MappedSchema
import net.corda.core.schemas.PersistentState
import net.corda.core.schemas.QueryableState
import net.corda.core.serialization.CordaSerializable
import net.corda.core.transactions.LedgerTransaction
import java.time.Instant
/**
* Contracts that verifies an evolution of [MembershipState]s. Only an evolution of [MembershipState]s is verified, not of their metadata.
* To verify evolution of a membership metadata, users can:
* 1. override responding flows at the BNO's side and put a custom verification logic in there (off-ledger verification)
* 2. override [MembershipContract] and add a custom verification logic into a new contract (on-ledger verification), for example:
*
* class MyMembershipContract : MembershipContract {
* // ........
*
* override fun verifyAmend(tx : LedgerTransaction, command : CommandWithParties<Commands>, outputMembership : MembershipState<*>, inputMembership : MembershipState<*>) {
* super.verifyAmend(tx, command, outputMembership, inputMembership)
* // custom logic goes in here
* }
* }
*/
open class OrdineContract : Contract {
companion object {
// TO DO da cambiare
const val CONTRACT_NAME = "com.r3.businessnetworks.ordine.states.OrdineContract"
}
open class Commands : CommandData, TypeOnlyCommandData() {
class Immesso : Commands()
class Rifiutato : Commands()
class Autorizzato : Commands()
}
override fun verify(tx : LedgerTransaction) {
val command = tx.commands.requireSingleCommand<Commands>()
val output = tx.outputs.single { it.data is OrdineState<*> }
val outputOrdine = output.data as OrdineState<*>
requireThat {
"Timestamp modifica deve essere maggiore o uguale Timestamp inserimento" using (outputOrdine.modificato >= outputOrdine.inserito)
"Autorizzatore e Ordinante devono essere participants" using (outputOrdine.participants.toSet() == setOf(outputOrdine.ordinante, outputOrdine.autorizzatore))
"Output state deve essere validato con ${contractName()}" using (output.contract == contractName())
if (!tx.inputs.isEmpty()) {
val input = tx.inputs.single()
val inputState = input.state.data as OrdineState<*>
"Participants of input and output states devono essere gli stessi" using (outputOrdine.participants.toSet() == input.state.data.participants.toSet())
"Input state hdeve essere validato con ${contractName()}" using (input.state.contract == contractName())
"Input and output states devono avere lo stessa timestamp di inserimento" using (inputState.inserito == outputOrdine.inserito)
"Input and output states devono avere la stessa linear IDs" using (inputState.linearId == outputOrdine.linearId)
"Output state's modificato timestamp deve essere maggiore del input's" using (outputOrdine.modificato > inputState.modificato)
}
}
when (command.value) {
is Commands.Immesso -> verifyImmesso(tx, command, outputOrdine)
is Commands.Rifiutato -> verifyRifiutato(tx, command, outputOrdine, tx.inputsOfType<OrdineState<*>>().single())
is Commands.Autorizzato -> verifyAutorizzato(tx, command, outputOrdine, tx.inputsOfType<OrdineState<*>>().single())
else -> throw IllegalArgumentException("Unsupported command ${command.value}")
}
}
// custom implementations should be able to specify their own contract names
open fun contractName() = CONTRACT_NAME
open fun verifyImmesso(tx : LedgerTransaction, command : CommandWithParties<Commands>, outputOrdine : OrdineState<*>) = requireThat {
"Entrambi Autorizzatore e ordinante devono firmare la transazione di ordine Immesso" using (command.signers.toSet() == outputOrdine.participants.map { it.owningKey }.toSet() )
"La transazione di ordine Immesso non deve contenrere inputs" using (tx.inputs.isEmpty())
"La transazione di ordine Immesso deve contenere un output state in stato IMMESSO" using (outputOrdine.isImmesso())
}
open fun verifyRifiutato(tx : LedgerTransaction, command : CommandWithParties<Commands>, outputOrdine : OrdineState<*>, inputOrdine : OrdineState<*>) = requireThat {
"Solo l'Autorizzatore deve firmare la transaction RIFIUTATO" using (command.signers.toSet() == setOf(outputOrdine.autorizzatore.owningKey))
"Input state della transaction RIFIUTATO non deve essere RIFIUTATO" using (!inputOrdine.isRifiutato())
"Output state di una transaction RIFIUTATO deve essere RIFIUTATO" using (outputOrdine.isRifiutato())
"Input and output states della transaction RIFIUTATO devono avere lo stesso Blobdata" using (inputOrdine.ordineBlobdata == outputOrdine.ordineBlobdata)
}
open fun verifyAutorizzato(tx : LedgerTransaction, command : CommandWithParties<Commands>, outputOrdine : OrdineState<*>, inputOrdine : OrdineState<*>) = requireThat {
"Solo Autorizzatore deve firmare la transaction AUTORIZZATO" using (command.signers.toSet() == setOf(outputOrdine.autorizzatore.owningKey))
"Input state di transaction AUTORIZZATO non deve essere già AUTORIZZATO" using (!inputOrdine.isAutorizzato())
"Output state di transaction AUTORIZZATO deve essere AUTORIZZATO" using (outputOrdine.isAutorizzato())
"Input and output states di transaction AUTORIZZATO deve avere gli stessi Blobdata" using (inputOrdine.ordineBlobdata == outputOrdine.ordineBlobdata)
}
}
/**
* Rappresenta un ordine di pagamento sul ledger. [ordineBlobdata] al momento è un blob di dati da definire/gestire.
*
* @param ordinante identity dell'ordinante
* @param autorizzatore identity di colui che autorizza
* @param inserito timestamp quando l'ordine di pagamento è stato immesso
* @param modificato timestamp quando lo stato è stato modificato l'ultima volta
* @param status status of the state, i.e. AUTORIZZATO, RIFIUTATO, IMMESSO etc.
*/
@BelongsToContract(OrdineContract::class)
data class OrdineState<out T : Any>(val ordinante : Party,
val autorizzatore : Party,
val ordineBlobdata : T,
val inserito : Instant = Instant.now(),
val modificato : Instant = inserito,
val status : OrdineStatus = OrdineStatus.IMMESSO,
override val linearId : UniqueIdentifier = UniqueIdentifier()) : LinearState, QueryableState {
override fun generateMappedObject(schema : MappedSchema) : PersistentState {
return when (schema) {
is OrdineStateSchemaV1 -> OrdineStateSchemaV1.PersistentOrdineState(
ordinante = this.ordinante,
autorizzatore = this.autorizzatore,
status = this.status
)
else -> throw IllegalArgumentException("Unrecognised schema $schema")
}
}
override fun supportedSchemas() = listOf(OrdineStateSchemaV1)
override val participants = listOf(autorizzatore, ordinante)
fun isRifiutato() = status == OrdineStatus.RIFIUTATO
fun isImmesso() = status == OrdineStatus.IMMESSO
fun isAutorizzato() = status == OrdineStatus.AUTORIZZATO
}
@CordaSerializable
object OrdineStateSchemaV1 : MappedSchema(schemaFamily = OrdineState::class.java, version = 1, mappedTypes = listOf(PersistentOrdineState::class.java)) {
@Entity
@Table(name = "ordine_states")
class PersistentOrdineState (
@Column(name = "ordinante_name")
var ordinante: Party,
@Column(name = "autorizzatore_name")
var autorizzatore: Party,
@Column(name = "status")
var status: OrdineStatus) : PersistentState()
}
/**
* Stati di un Ordine di Pagamento.
*
* [IMMESSO] - l'ordinante ha IMMESSO l'ordine di pagamento, ed è in attesa di autorizzazione.
* [AUTORIZZATO] - l'ordine di pagamento è stato AUTORIZZATO
* [RIFIUTATO] - l'ordine di pagamento è stato RIFIUTATO
*/
@CordaSerializable
enum class OrdineStatus {
IMMESSO, AUTORIZZATO, RIFIUTATO
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment