NebulousLabs / Sia

Blockchain-based marketplace for file storage. Project has moved to GitLab: https://gitlab.com/NebulousLabs/Sia
https://sia.tech
MIT License
2.71k stars 439 forks source link

Interfaces for Core #160

Closed DavidVorick closed 9 years ago

DavidVorick commented 9 years ago

I was thinking about the best way to do this, and you mentioned interfaces. I'm just spitballing ideas here because I'm not in love with any of them.

So we have a Core that takes some interfaces as arguments. There's a wallet interface, a host interface, etc. And each interface has a bunch of functions, which don't all need to be implemented. Sometimes the wallet can return an error "not implemented" if you call something like "multi-spend" which would ideally be implemented in a full fledged wallet but not necessarily in a simpler and more minimal wallet.

Second thought, you have each type of wallet and host and renter be it's own package. So you'd have a bunch of packages, simplewallet, reedsolomonrenter, staticpricehost, etc, and the user gets to pick which wallet they want to use. Each of the things implements the interfaces in Core, but only the functions that they want.

And maybe things like the host and the core should be exported, so that the user can interact with them directly. The problem then is that you need to trust the user not to screw up any of the mutex stuff.

Ultimately I'm not sure that interfaces are the best approach, because I'm not sure that we should be making things so extremely modular. There's no value to using an interface if you only ever intend to have one type of object that implements that interface, so if we are using an interface it means that we do plan on having multiple different types of objects implement each interface, and the Core struct will have the interfaces as variables instead of the objects themselves.

I think we can achieve our goals by carefully picking which functions are exported and making these functions generic enough that as the hosts, renters, miners, and wallets evolve you don't need to change very many things in the underlying code.

lukechampine commented 9 years ago

And each interface has a bunch of functions, which don't all need to be implemented.

Well, if not all the functions are implemented, Go won't recognize that type as implementing the interface. However, you can have, say, a Wallet interface and a Multi-Spend interface, and make the function just take a Wallet. Then you can use reflection to determine if the type also implements the Multi-Spend interface. The standard library does this in a few places, notably the io package.

There's no value to using an interface if you only ever intend to have one type of object that implements that interface

There is one more advantage, which is that it makes it easier to modify the underlying functionality. For example, We could have a DB interface:

type DB interface {
    Save(*Blockchain) error
    Load(string) (*Blockchain, error)
    Store(Block) error
    Fetch(BlockID) Block
    // or whatever
}

So we start out using LevelDB, and build a layer around it to implement DB. But then later we decide to switch to Bolt. If we were calling LevelDB functions directly, we would have to run around rewriting all that code to use Bolt functions instead. But if we use an interface, all we have to do is make Bolt implement DB.

Ultimately, I don't expect we'll be making heavy use of interfaces. siad doesn't need them, because it builds directly upon core. And the core doesn't need many of them internally, because it works with well-established types. So the place I think interfaces are most needed are wherever core needs something from external sources, like siad or encoding. The DB is one example. Another would be if we decide to use a different hashing scheme.

Here's the real advantage of interfaces: siad imports core, so core can't import siad. That means we can't even write core functions that take siad.Type arguments. But we can define interfaces in core and implement them in siad. So core doesn't know exactly what type it's getting, but it can be sure that it implements all the functionality that it needs.

DavidVorick commented 9 years ago

I do like the last advantage. Also, interfaces are generally self-documenting, which makes them easier to work with.

DavidVorick commented 9 years ago

I'm going to consider this closed, since the peer stuff we haven't fully agreed on