diff --git a/txemulator/trace.go b/txemulator/trace.go index f736151f..3f8a6a36 100644 --- a/txemulator/trace.go +++ b/txemulator/trace.go @@ -168,6 +168,18 @@ func accountCode(account tlb.ShardAccount) *boc.Cell { return &cell } +func msgStateInitCode(msg tlb.Message) *boc.Cell { + if !msg.Init.Exists { + return nil + } + code := msg.Init.Value.Value.Code + if !code.Exists { + return nil + } + cell := code.Value.Value + return &cell +} + func (t *Tracer) Run(ctx context.Context, message tlb.Message) (*TxTree, error) { if t.counter >= t.limit { return nil, fmt.Errorf("to many iterations: %v/%v", t.counter, t.limit) @@ -195,8 +207,11 @@ func (t *Tracer) Run(ctx context.Context, message tlb.Message) (*TxTree, error) return nil, err } } - var publicLibs map[tongo.Bits256]*boc.Cell - if code := accountCode(state); code != nil { + publicLibs := map[tongo.Bits256]*boc.Cell{} + for _, code := range []*boc.Cell{accountCode(state), msgStateInitCode(message)} { + if code == nil { + continue + } hashes, err := FindLibraries(code) if err != nil { return nil, err @@ -206,7 +221,9 @@ func (t *Tracer) Run(ctx context.Context, message tlb.Message) (*TxTree, error) if err != nil { return nil, err } - publicLibs = libs + for hash, cell := range libs { + publicLibs[hash] = cell + } } } if len(publicLibs) > 0 { @@ -218,7 +235,6 @@ func (t *Tracer) Run(ctx context.Context, message tlb.Message) (*TxTree, error) return nil, err } } - // TODO: look up libraries in the msg's stateInit, so if it's a deploy contract message, Emulate() won't fail. result, err := t.e.Emulate(state, message) if err != nil { return nil, err diff --git a/txemulator/trace_test.go b/txemulator/trace_test.go index f20412b6..9c6f83e4 100644 --- a/txemulator/trace_test.go +++ b/txemulator/trace_test.go @@ -89,10 +89,40 @@ func TestEmulate(t *testing.T) { t.Fatalf("tx failed") } if len(tree.Children) != 1 { - t.Fatalf("expected tx to has 1 child") + t.Fatalf("expected tx to have 1 child") } second := tree.Children[0].TX if !second.IsSuccess() { t.Fatalf("second tx failed") } } + +func TestEmulate_ToUninitContract(t *testing.T) { + // this message is a contract-deploy message for "EQCeL1iwCkDZFIN_w3corAk0HLyDFoFKI9sU-zbpBtsqxwd0", which uses a public library. + c, err := boc.DeserializeSinglRootBase64("te6ccgEBAwEAtQACz4gBPF6xYBSBsikG/4buUVgSaDl5Bi0ClEe2KfZt0g22VY4RgapDllUZl8zqKhXvay+jOBYBQZIi9WgRbY+2Sm0k2sZOpAdn+updccco1ndWlewrbU2NzSA29wvefa9KuFSWIeAAAAAQAQIIQgJYfMeJ7/HIT0bsN5fkX8gJoU/1riTx4MemqZzJ3JBh/wBIAAAAAMRoaqYjP2gxcScR+ePyWBCVr/kSa65hTLtoJPi7y8sP") + if err != nil { + t.Fatal(err) + } + var m tlb.Message + if err = tlb.Unmarshal(c, &m); err != nil { + t.Fatal(err) + } + client, err := liteapi.NewClient(liteapi.Mainnet(), liteapi.FromEnvs()) + if err != nil { + t.Fatal(err) + } + emulator, err := NewTraceBuilder(WithAccountsSource(client)) + if err != nil { + t.Fatalf("NewTraceBuilder() failed: %v", err) + } + tree, err := emulator.Run(context.Background(), m) + if err != nil { + t.Fatalf("Run() failed: %v", err) + } + if !tree.TX.IsSuccess() { + t.Fatalf("tx failed") + } + if len(tree.Children) != 0 { + t.Fatalf("expected tx to have no children") + } +}