I was recently working with state machines over Nostr for coordination between parties. The result is a distributed, event-driven, consistent state management protocol that can be used to model any process. It is also auditable, making it interesting for scientific and supply chain applications. Additionally, it enables the deployment of *smart contract-like* functionalities without the blockchain buzz or shitcoinery. I would love to hear your thoughts.

This project was originally inspired by a talk from nostr:npub1nyxg7ps82r86u0gunspsn8u8uuskh6sut77tulcqljue7rr7m6hquzh9ph during the last nostr:nprofile1qqsg8kve59yxyhpa9wupntesvnq0dgfd0k5g769jc6fzrua8gct36xgpzpmhxue69uhkumewwd68ytnrwghszrnhwden5te0dehhxtnvdakz7p5n66j 5.

It's also my first time using ngit ๐Ÿ”ฅ

https://gitworkshop.dev/npub1gzuushllat7pet0ccv9yuhygvc8ldeyhrgxuwg744dn5khnpk3gs3ea5ds/relay.ngit.dev/nsm

Reply to this note

Please Login to reply.

Discussion

If you want smart contracts you can have them on Nostr with state machines.

nostr:nevent1qqs0vm93ldes49l9q30z6ku79c3dy6rdjvq2d7wdea20a5dftyp67zskpsugr

look like or similar to nostRocket without the goal of changing the world ๐Ÿค™

Well if the world could be modeled as state machine... ๐Ÿค”๐Ÿคฃ

the states will be council llm ๐Ÿ˜…

Would it work for voting?

Yes, with a well-designed state machine definition, you can model a voting scheme to meet the requirements you need. For example, voters can publish vote events as transitions, or in any other way. The state machine can base its state transitions on a period of time during which voting is open. Once that time passes, the state machine can change its state to "counting," for example, and finally to "closed" once the counting process is finished. There are many different ways you could model the voting process, but you can certainly model it as a state machine.

would be really interested in a deep dive of how the smart contract like things work

Sure, we can delve deep into this, but to start, I can propose a simple state machine that uses an oracle and acts as a "smart contract" for a betting use case.

The state machine definition can include in its context the public keys of the participants and an oracle. Ideally, the oracle should also have a pubkey. In the context, you can also include other metadata required for the entire process, such as the date when the match that you are betting on starts and ends. With this context, both betting users are defined along with an oracle (or a series of them), and they can pay and scrow managed by the state machine. The state machine then initializes with a state like 'loaded'. When the match start date arrives, the state machine transitions to the 'started' state. After the oracle publishes the results and the match ends, the state machine computes the results and, based on that, determines the next state. For example, it could transition to 'finished' if there are no issues and one of the participants wins, or to 'dispute' if there is some kind of dispute, or to 'cancelled', etc. This is just a simple example, and there are certainly more nuances to consider, but at a high level, this is how a smart contract-like state machine can work.

The benefits of implementing this on Nostr with the concepts of my specification are that the state can be custodied by multiple parties acting as 'watchtowers', and every transition is auditable

Will definitely be delving into this. Love how you're always pushing the boundaries at lower levels and enabling innovative qpps to be built on top!

Thanks man, appreciate your words ๐Ÿ’› Let me know if you play with this :)

Thanks man appreciate it ๐Ÿงก

Just answered back ๐Ÿ‘Œ

Kind 30078 is already in use per NIP 78 fyi

Yes, I'm aware. It's chosen consciously as the snapshots are some sort of app specific data

I mean, so is everything. But in this context app specific means "not intended to be interoperable"

Yes exactly, seems appropriate for me

Then why write a spec?

To define what kind to use. I didn't consider that polluting the kind list was a good idea specifically for an event that might have very different shapes as every state machine state snapshot might have different shapes, and picked the only agnostic kind in nostr for the only replaceable event defined in the spec.

Eh, it's not polluting, that's what kinds are for, is to partition the protocol. But I see what you mean, if the schema varies there really is not a lot of interoperability.

Also, how does having to resort to authority for resolving forks not undermine the whole premise? Malicious forks (with forged timestamps) can be created at any point, forcing resolution and possibly denial of service. I can't help but see the double spend problem here.

Yes, this is a complex problem and it took me some time until I felt comfortable with any solution. Currently, the spec relies on the fact that users and state machines can define their own model of trust. The key here is that in the state machine definition event, 'state custodians' are defined. These state custodians are legit sources that are established in the state machine definition event and are something you can agree upon with the participants in the state machine. There might also be cases where one of the state custodians is a third-party service that offers to be a 'watchtower.' But as I mentioned, there is nothing imposed in the spec, so for each use case, users can choose the model that best suits their requirements. Then, every transition references the previous one, creating a DAG, and the state snapshot references the head, so there we have a deterministic tie-breaker. There is also a section in the spec about this: '7. Conflict Resolution and Chain Integrity' that explains all of this in detail. Let me know what do you think about this, and thanks for the feedback

Yeah, that was the section I was referring to. Multiple watchtower-type custodians helps (similar to relays), but they're still picked by the creator of the state machine, so they could be malicious. Nostr works because truth is relative, but in this case users can't pick their own custodian without risking an unresolved fork

Can you imagine a better way to solve this issue? My rationale behind this is that users agree beforehand who are going to be the state custodians, who at the end are the ones responsible for keeping the state coherence. The creator of the state machine is not transcendental; for example, if we both are going to participate in some sort of process, contract, or any other thing modeled by a state machine, we can both be state custodians and have the same responsibility. The creator doesn't matter at this point. Unresolved forks are a risk, but I think this model correctly places the responsibility on the participants, the state custodians, and does not enforce any specific requirements. I see these state machines as a way to define an agreement declaratively with all its possible states and conditions. This is a low level primitive to build things on top, and requirements for specific use cases like strong security of the state and dispute resolution should be application specific

Use a blockchain haha

I think this model probably has a lot of possible failure modes, especially when it comes to actual implementation. In the case of two participants, where a fork means the contract is over, that can work. But usually the function of a smart contract is to force agreement on something.

Anyway, I'm skeptical, but don't let that stop you. It's an interesting project. If we can indeed figure out a way to have consensus on nostr it would be germane to my key rotation nip, which makes some massive compromises on centralization in order to work: https://github.com/nostr-protocol/nips/pull/2137