New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
protocol/vm: remove the dependency on protocol/bc #797
Changes from 4 commits
93fd5e5
df6946c
4b58b74
a0f1323
7bfd324
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
package bc | ||
|
||
import ( | ||
"bytes" | ||
|
||
"chain/protocol/vm" | ||
) | ||
|
||
func NewBlockVMContext(block *Block, prog []byte, args [][]byte) *vm.Context { | ||
blockHash := block.Hash().Bytes() | ||
return &vm.Context{ | ||
VMVersion: 1, | ||
Code: prog, | ||
Arguments: args, | ||
|
||
BlockHash: &blockHash, | ||
BlockTimeMS: &block.TimestampMS, | ||
NextConsensusProgram: &block.ConsensusProgram, | ||
} | ||
} | ||
|
||
func NewTxVMContext(tx *Tx, index uint32, prog Program, args [][]byte) *vm.Context { | ||
var ( | ||
txSigHash = tx.SigHash(index).Bytes() | ||
numResults = uint64(len(tx.Results)) | ||
assetID = tx.Inputs[index].AssetID() | ||
assetIDBytes = assetID[:] | ||
amount = tx.Inputs[index].Amount() | ||
inputRefDataHash = hashData(tx.Inputs[index].ReferenceData).Bytes() | ||
txRefDataHash = hashData(tx.ReferenceData).Bytes() | ||
) | ||
|
||
checkOutput := func(index uint64, refdatahash []byte, amount uint64, assetID []byte, vmVersion uint64, code []byte) (bool, error) { | ||
if index >= uint64(len(tx.Outputs)) { | ||
return false, vm.ErrBadValue | ||
} | ||
o := tx.Outputs[index] | ||
if o.AssetVersion != 1 { | ||
return false, nil | ||
} | ||
if o.Amount != uint64(amount) { | ||
return false, nil | ||
} | ||
if o.VMVersion != uint64(vmVersion) { | ||
return false, nil | ||
} | ||
if !bytes.Equal(o.ControlProgram, code) { | ||
return false, nil | ||
} | ||
if !bytes.Equal(o.AssetID[:], assetID) { | ||
return false, nil | ||
} | ||
if len(refdatahash) > 0 { | ||
h := hashData(o.ReferenceData) | ||
if !bytes.Equal(h[:], refdatahash) { | ||
return false, nil | ||
} | ||
} | ||
return true, nil | ||
} | ||
|
||
result := &vm.Context{ | ||
VMVersion: prog.VMVersion, | ||
Code: prog.Code, | ||
Arguments: args, | ||
|
||
TxVersion: &tx.Version, | ||
|
||
TxSigHash: &txSigHash, | ||
NumResults: &numResults, | ||
AssetID: &assetIDBytes, | ||
Amount: &amount, | ||
MinTimeMS: &tx.MinTime, | ||
MaxTimeMS: &tx.MaxTime, | ||
InputRefDataHash: &inputRefDataHash, | ||
TxRefDataHash: &txRefDataHash, | ||
InputIndex: &index, | ||
CheckOutput: checkOutput, | ||
} | ||
switch inp := tx.Inputs[index].TypedInput.(type) { | ||
case *IssuanceInput: | ||
result.Nonce = &inp.Nonce | ||
case *SpendInput: | ||
spentOutputID := tx.TxHashes.SpentOutputIDs[index][:] | ||
result.SpentOutputID = &spentOutputID | ||
} | ||
|
||
return result | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
package vm | ||
|
||
// Context contains the execution context for the virtual machine. | ||
// | ||
// Most fields are pointers and are not required to be present in all | ||
// cases. A nil pointer means the value is absent in that context. If | ||
// an opcode executes that requires an absent field to be present, it | ||
// will return ErrContext. | ||
// | ||
// By convention, variables of this type have the name context, _not_ | ||
// ctx (to avoid confusion with context.Context). | ||
type Context struct { | ||
VMVersion uint64 | ||
Code []byte | ||
Arguments [][]byte | ||
|
||
// TxVersion must be present when verifying transaction components | ||
// (such as spends and issuances). | ||
TxVersion *uint64 | ||
|
||
// These fields must be present when verifying block headers. | ||
|
||
BlockHash *[]byte | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would be nice to document how the optional fields work, maybe in the godoc for type Context or func Verify. Something brief would be fine, e.g. something like what I wrote in an earlier comment:
|
||
BlockTimeMS *uint64 | ||
NextConsensusProgram *[]byte | ||
|
||
// Fields below this point are required by particular opcodes when | ||
// verifying transaction components. | ||
|
||
TxSigHash *[]byte | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A slice is capable of being nil on its own, so if you want, the slice fields here don't have to be pointers. (Same as for the func field). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The slices that are hashes could be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There's a difference between a nil slice (which would mean absent) and a zero-length but non-nil slice (which would mean empty and present). So they don't need to be pointers to slices. But I don't feel strongly about it, I just wanted to raise the possibility. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh good point. But, I don't know, I don't much like the idea of relying on the difference between |
||
NumResults *uint64 | ||
AssetID *[]byte | ||
Amount *uint64 | ||
MinTimeMS *uint64 | ||
MaxTimeMS *uint64 | ||
InputRefDataHash *[]byte | ||
TxRefDataHash *[]byte | ||
InputIndex *uint32 | ||
Nonce *[]byte | ||
SpentOutputID *[]byte | ||
|
||
CheckOutput func(index uint64, data []byte, amount uint64, assetID []byte, vmVersion uint64, code []byte) (bool, error) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Out of scope for this PR (because it would be a spec change), but it seems strange to me that this one check should abort the whole vm while all the other checks just return false. If my program asks whether there's an output at position 5, and there are only two outputs, the answer is "no, there isn't".
So yeah, it would be great (IMO) if this function could be a simple predicate. But since it can't, it might look a little cleaner to have it return
error
that's one ofvm.ErrBadValue
orvm.NotFound
ornil
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, now that you mention it, the spec is silent on what to when
index
is out of range. (And the updated spec on thetxgraph-spec
branch is similarly silent but includes outdated language about "the number of outputs.")I agree that just returning
false
would be preferable. @danrobinson @oleganza would this be acceptable, and if so will you clarify the spec?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The spec says
which I interpret to explicitly require the current behavior.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this line says that CHECKOUTPUT should fail execution if asked to find out-of-bounds output:
Same as in txgraph spec.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@bobg i guess "outputs" should be "destinations" and moved into appropriate part (under "if we are in the Mux").
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
😊
Carry on...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you have thoughts on my actual suggestion above?